Android 11 introduce la possibilità di utilizzare AIDL per gli HAL in Android. Ciò rende è possibile implementare parti di Android senza l'HIDL. Transizione degli HAL per l'utilizzo dell'AIDL esclusivamente ove possibile (quando gli HAL upstream utilizzano HIDL, è necessario utilizzare HIDL).
HAL che utilizzano AIDL per comunicare tra componenti del framework, ad esempio quelli in
system.img
e i componenti hardware, come quelli in vendor.img
, devono utilizzare
AIDL stabile. Tuttavia, per comunicare all'interno di una partizione, ad esempio da una
dall'HAL a un altro, non esistono limitazioni relative al meccanismo IPC da utilizzare.
Motivazione
L'AIDL è più lunga di quella HIDL e viene utilizzata in molti altri luoghi, come tra componenti di framework Android o nelle app. Ora che l'AIDL è stabile è possibile implementare un intero stack con un singolo runtime IPC. AIDL ha anche un sistema di controllo delle versioni migliore rispetto a HIDL.
- Utilizzare un singolo linguaggio IPC significa avere una sola cosa da imparare, eseguire il debug ottimizzare e proteggere.
- AIDL supporta il controllo delle versioni in loco per i proprietari di un'interfaccia:
- I proprietari possono aggiungere metodi alla fine delle interfacce o campi ai pacchetti parcelable. Ciò significa che, nel corso degli anni e anche nel corso dell'anno, è stato più facile su base annua è inferiore (i tipi possono essere modificati in loco e non necessarie librerie aggiuntive per ogni versione dell'interfaccia).
- Le interfacce delle estensioni possono essere collegate in fase di runtime anziché nel tipo di sistema, pertanto non è necessario ridefinire le estensioni downstream le versioni più recenti delle interfacce.
- Un'interfaccia AIDL esistente può essere utilizzata direttamente quando il proprietario sceglie di stabilizzarlo. In precedenza, un'intera copia dell'interfaccia doveva creati in HIDL.
Crea in base al runtime AIDL
AIDL ha tre backend diversi: Java, NDK e CPP. Per utilizzare il modello AIDL stabile, devi
usa sempre la copia di sistema di libbinder all'indirizzo system/lib*/libbinder.so
e parla
il giorno /dev/binder
. Per il codice sull'immagine del fornitore, questo significa che libbinder
(dal VNDK) non può essere utilizzato: questa libreria ha un'API C++ instabile e
interni instabili. Il codice nativo del fornitore deve invece usare il backend NDK di
AIDL, collegamento a libbinder_ndk
(supportato dal sistema libbinder.so
),
e si collegano alle librerie NDK create dalle voci aidl_interface
. Per
i nomi esatti dei moduli, vedi
regole di denominazione dei moduli.
Scrivi un'interfaccia AIDL HAL
Affinché un'interfaccia AIDL possa essere usata tra il sistema e il fornitore, l'interfaccia deve avere due modifiche:
- Ogni definizione del tipo deve essere annotata con
@VintfStability
. - La dichiarazione
aidl_interface
deve includerestability: "vintf",
.
Solo il proprietario di un'interfaccia può apportare queste modifiche.
Quando apporti queste modifiche, l'interfaccia deve essere in
manifest di VINTF. Testa questo (e relativi
come la verifica del blocco delle interfacce rilasciate) utilizzando
Test VTS vts_treble_vintf_vendor_test
. Puoi usare un @VintfStability
senza questi requisiti richiamando
AIBinder_forceDowngradeToLocalStability
nel backend NDK,
android::Stability::forceDowngradeToLocalStability
nel backend C++,
o android.os.Binder#forceDowngradeToSystemStability
nel backend Java
su un oggetto binder prima che venga inviato a un altro processo. Eseguire il downgrade di un servizio
alla stabilità del fornitore non è supportata in Java perché tutte le app vengono eseguite in un sistema
contesto.
Inoltre, per la massima portabilità del codice e per evitare potenziali problemi come come librerie aggiuntive non necessarie, disabilita il backend CPP.
Tieni presente che l'utilizzo di backends
nell'esempio di codice riportato di seguito è corretto, in quanto
sono tre backend (Java, NDK e CPP). Il codice seguente indica come selezionare
in particolare del backend CPP, per disabilitarlo.
aidl_interface: {
...
backends: {
cpp: {
enabled: false,
},
},
}
Trova le interfacce AIDL HAL
Le interfacce AOSP stabili AIDL per gli HAL si trovano nelle stesse directory di base
Interfacce HIDL, in aidl
cartelle.
- hardware/interfacce
- framework/hardware/interfacce
- sistema/hardware/interfacce
Dovresti inserire le interfacce delle estensioni in altri hardware/interfaces
sottodirectory in vendor
o hardware
.
Interfacce delle estensioni
Android dispone di una serie di interfacce AOSP ufficiali in ogni release. Se Android i partner vogliono aggiungere funzionalità a queste interfacce, non devono cambiare direttamente perché questo significherebbe che il loro runtime Android incompatibili con il runtime Android AOSP. Per i dispositivi GMS, evitare di cambiare queste interfacce sono anche ciò che garantisce che l'immagine GSI possa continuare a funzionare.
Le estensioni possono essere registrate in due modi diversi:
- per l'esecuzione, consulta le estensioni collegate.
- indipendenti, registrati a livello globale e in VINTF.
Tuttavia, un'estensione viene registrata quando è specifico del fornitore (ovvero non fa parte upstream AOSP) utilizzano l'interfaccia, non c'è possibilità di unire in conflitto. Tuttavia, quando le modifiche downstream ai componenti AOSP upstream vengono potrebbero verificarsi conflitti di unione. Si consiglia di adottare le seguenti strategie:
- le aggiunte all'interfaccia potranno essere upstreaming su AOSP nella prossima versione
- aggiunte di interfacce che consentono maggiore flessibilità, senza conflitti di unione possono essere upstream nella prossima release
Estensione parcelableer: ParcelableHolder
ParcelableHolder
è un Parcelable
che può contenere un altro Parcelable
.
Il caso d'uso principale di ParcelableHolder
è rendere un elemento Parcelable
estensibile.
Ad esempio, un'immagine che gli utenti di implementare dei dispositivi si aspettano di poter estendere
Parcelable
definito da AOSP, AospDefinedParcelable
, per includere il relativo valore aggiunto
le funzionalità di machine learning.
In precedenza, senza ParcelableHolder
, gli implementatori dei dispositivi non potevano modificare
un'interfaccia AIDL stabile definita AOSP perché sarebbe un errore aggiungerne altre
campi:
parcelable AospDefinedParcelable {
int a;
String b;
String x; // ERROR: added by a device implementer
int[] y; // added by a device implementer
}
Come indicato nel codice precedente, questa pratica non funziona perché i campi aggiunto dall'implementatore del dispositivo potrebbe verificarsi un conflitto quando nelle prossime versioni di Android.
Utilizzando ParcelableHolder
, il proprietario di un parcelable può definire un'estensione
punto in Parcelable
.
parcelable AospDefinedParcelable {
int a;
String b;
ParcelableHolder extension;
}
Gli utenti che implementano il dispositivo possono quindi definire il proprio Parcelable
per il proprio
.
parcelable OemDefinedParcelable {
String x;
int[] y;
}
Infine, il nuovo Parcelable
può essere collegato all'elemento Parcelable
originale con
il campo ParcelableHolder
.
// Java
AospDefinedParcelable ap = ...;
OemDefinedParcelable op = new OemDefinedParcelable();
op.x = ...;
op.y = ...;
ap.extension.setParcelable(op);
...
OemDefinedParcelable op = ap.extension.getParcelable(OemDefinedParcelable.class);
// C++
AospDefinedParcelable ap;
OemDefinedParcelable op;
std::shared_ptr<OemDefinedParcelable> op_ptr = make_shared<OemDefinedParcelable>();
ap.extension.setParcelable(op);
ap.extension.setParcelable(op_ptr);
...
std::shared_ptr<OemDefinedParcelable> op_ptr;
ap.extension.getParcelable(&op_ptr);
// NDK
AospDefinedParcelable ap;
OemDefinedParcelable op;
ap.extension.setParcelable(op);
...
std::optional<OemDefinedParcelable> op;
ap.extension.getParcelable(&op);
// Rust
let mut ap = AospDefinedParcelable { .. };
let op = Rc::new(OemDefinedParcelable { .. });
ap.extension.set_parcelable(Rc::clone(&op));
...
let op = ap.extension.get_parcelable::<OemDefinedParcelable>();
Nomi istanze server AIDL HAL
Per convenzione, i servizi AIDL HAL hanno un nome istanza del formato
$package.$type/$instance
. Ad esempio, un'istanza della vibrazione HAL è
registrato come android.hardware.vibrator.IVibrator/default
.
Scrivi un server AIDL HAL
@VintfStability
I server AIDL devono essere dichiarati nel manifest VINTF, ad
esempio come questo:
<hal format="aidl">
<name>android.hardware.vibrator</name>
<version>1</version>
<fqname>IVibrator/default</fqname>
</hal>
In caso contrario, dovrebbero registrare un servizio AIDL normalmente. Durante l'esecuzione di VTS test, è previsto che siano disponibili tutti gli HAL AIDL dichiarati.
Scrivi un client AIDL
I client AIDL devono dichiararsi nella matrice di compatibilità, ad esempio nel seguente modo:
<hal format="aidl" optional="true">
<name>android.hardware.vibrator</name>
<version>1-2</version>
<interface>
<name>IVibrator</name>
<instance>default</instance>
</interface>
</hal>
Conversione di un HAL esistente da HIDL ad AIDL
Utilizza lo strumento hidl2aidl
per convertire un'interfaccia HIDL in formato AIDL.
hidl2aidl
funzionalità:
- Crea file
.aidl
in base ai file.hal
per il pacchetto specificato - Crea regole di build per il pacchetto AIDL appena creato con tutti i backend attivata
- Crea metodi di traduzione nei backend Java, CPP e NDK per la traduzione dai tipi HIDL a quelli AIDL
- Crea regole di build per tradurre le librerie con le dipendenze richieste
- Crea asserzioni statiche per garantire che gli enumeratori HIDL e AIDL abbiano il gli stessi valori nei backend CPP e NDK
Per convertire un pacchetto di file .hal in file .aidl, procedi nel seguente modo:
Crea lo strumento che si trova in
system/tools/hidl/hidl2aidl
.Creare questo strumento partendo dalla fonte più recente offre il un'esperienza senza intervento manuale. Puoi utilizzare la versione più recente per convertire le interfacce su versioni precedenti rami delle release precedenti.
m hidl2aidl
Esegui lo strumento con una directory di output seguita dal pacchetto da convertito.
Facoltativamente, utilizza l'argomento
-l
per aggiungere i contenuti di un nuovo file di licenza nella parte superiore di tutti i file generati. Assicurati di utilizzare la licenza e la data corrette.hidl2aidl -o <output directory> -l <file with license> <package>
Ad esempio:
hidl2aidl -o . -l my_license.txt android.hardware.nfc@1.2
Leggi i file generati e risolvi eventuali problemi relativi alla conversione.
conversion.log
contiene prima problemi non gestiti da risolvere.- I file
.aidl
generati potrebbero contenere avvisi e suggerimenti hanno bisogno di un intervento. Questi commenti iniziano con//
. - Cogli l'opportunità per ripulire e apportare miglioramenti al pacchetto.
- Controlla il
@JavaDerive
per gli elementi che potrebbero essere necessari, cometoString
oequals
.
Crea solo i target di cui hai bisogno.
- Disabilita i backend che non verranno utilizzati. Preferisci il backend NDK rispetto al CPP consulta la sezione sulla scelta del runtime.
- Rimuovi le librerie di traduzione o il codice generato che non verrà utilizzato.
Consulta Principali differenze AIDL/HIDL.
- L'utilizzo dell'
Status
integrata di AIDL e delle eccezioni in genere migliora il ed eliminare la necessità di un altro tipo di stato specifico per l'interfaccia. - Gli argomenti dell'interfaccia AIDL nei metodi non sono
@nullable
per impostazione predefinita, come avviene erano in HIDL.
- L'utilizzo dell'
SEPolicy per AIDL HAL
Un tipo di servizio AIDL visibile al codice del fornitore deve avere i campi
Attributo hal_service_type
. In caso contrario, la configurazione sepolicy è la stessa
come qualsiasi altro servizio AIDL (anche se esistono attributi speciali per gli HAL). Qui
esiste una definizione di esempio del contesto di un servizio HAL:
type hal_foo_service, service_manager_type, hal_service_type;
Per la maggior parte dei servizi definiti dalla piattaforma, è necessario specificare un contesto di servizio con
tipo è già stato aggiunto (ad esempio, android.hardware.foo.IFoo/default
sono già contrassegnati come hal_foo_service
). Tuttavia, se un client framework supporta
più nomi di istanze, occorre aggiungere altri nomi di istanza
file service_contexts
specifici per il dispositivo.
android.hardware.foo.IFoo/custom_instance u:object_r:hal_foo_service:s0
Gli attributi HAL devono essere aggiunti quando creiamo un nuovo tipo di HAL. Un HAL specifico
potrebbe essere associato a più tipi di servizi (ciascuno dei quali può
avere più istanze, come abbiamo appena illustrato). Per un HAL, foo
, abbiamo
hal_attribute(foo)
. Questa macro definisce gli attributi hal_foo_client
e
hal_foo_server
. Per un determinato dominio, i hal_client_domain
e
Le macro hal_server_domain
associano un dominio a un determinato attributo HAL. Per
Ad esempio, il server di sistema, che è un client di questo HAL, corrisponde al criterio
hal_client_domain(system_server, hal_foo)
. Un server HAL allo stesso modo include
hal_server_domain(my_hal_domain, hal_foo)
. Generalmente, per un determinato HAL
creiamo anche un dominio come hal_foo_default
come riferimento o
di esempio di HAL. Tuttavia, alcuni dispositivi utilizzano questi domini per i propri server.
La distinzione tra più domini per più server è importante solo se abbiamo
più server che gestiscono la stessa interfaccia e necessitano di un'autorizzazione diversa
nelle loro implementazioni. In tutte queste macro, hal_foo
non è effettivamente
un oggetto sepolicy. Viene invece utilizzato da queste macro per fare riferimento
il gruppo di attributi associati a una coppia di server client.
Tuttavia, finora non abbiamo associato hal_foo_service
e hal_foo
(la coppia di attributi di hal_attribute(foo)
). È associato un attributo HAL
con i servizi AIDL HAL utilizzando la macro hal_attribute_service
(gli HAL HIDL utilizzano
la macro hal_attribute_hwservice
). Ad esempio:
hal_attribute_service(hal_foo, hal_foo_service)
. Ciò significa che
I processi hal_foo_client
possono accedere all'HAL e hal_foo_server
processi possono registrare l'HAL. L'applicazione di queste regole di registrazione è
eseguite dal gestore contesto (servicemanager
). Nota: i nomi dei servizi
non sempre corrispondono agli attributi HAL. Ad esempio, potremmo vedere
hal_attribute_service(hal_foo, hal_foo2_service)
. In generale, però, dato che
Ciò implica che i servizi vengono sempre utilizzati insieme, potremmo valutare la rimozione
hal_foo2_service
e utilizzo di hal_foo_service
per tutti i nostri servizi
i contesti. La maggior parte degli HAL che impostano più hal_attribute_service
perché
il nome dell'attributo HAL originale non è abbastanza generico e non può essere modificato.
Riassumendo, un HAL di esempio ha il seguente aspetto:
public/attributes:
// define hal_foo, hal_foo_client, hal_foo_server
hal_attribute(foo)
public/service.te
// define hal_foo_service
type hal_foo_service, hal_service_type, protected_service, service_manager_type
public/hal_foo.te:
// allow binder connection from client to server
binder_call(hal_foo_client, hal_foo_server)
// allow client to find the service, allow server to register the service
hal_attribute_service(hal_foo, hal_foo_service)
// allow binder communication from server to service_manager
binder_use(hal_foo_server)
private/service_contexts:
// bind an AIDL service name to the selinux type
android.hardware.foo.IFooXxxx/default u:object_r:hal_foo_service:s0
private/<some_domain>.te:
// let this domain use the hal service
binder_use(some_domain)
hal_client_domain(some_domain, hal_foo)
vendor/<some_hal_server_domain>.te
// let this domain serve the hal service
hal_server_domain(some_hal_server_domain, hal_foo)
Interfacce delle estensioni collegate
È possibile collegare un'estensione a qualsiasi interfaccia binder, sia che si tratti di un'interfaccia di primo livello registrata direttamente con il gestore del servizio oppure è un'interfaccia secondaria. Quando ricevi un'estensione, devi verificare che il tipo di estensione sia corretto previsto. Le estensioni possono essere impostate solo dal processo che gestisce un binder.
Le estensioni allegate devono essere utilizzate ogni volta che un'estensione modifica il la funzionalità di un HAL esistente. Quando sono necessarie funzionalità completamente nuove, non è necessario usare questo meccanismo. È possibile eseguire un'interfaccia di estensione registrati direttamente con il gestore del servizio. Interfacce delle estensioni collegate hanno più senso quando sono collegati a interfacce secondarie, perché le gerarchie possono essere profonde o con istanze multiple. Utilizzo di un'estensione globale per il mirroring la gerarchia dell'interfaccia a riga di comando di un altro servizio richiederebbe dei dati per fornire funzionalità equivalenti alle estensioni collegate direttamente.
Per impostare un'estensione su binder, utilizza le seguenti API:
- Nel backend NDK:
AIBinder_setExtension
- Nel backend Java:
android.os.Binder.setExtension
- Nel backend CPP:
android::Binder::setExtension
- Nel backend Rust:
binder::Binder::set_extension
Per ottenere un'estensione su un binder, usa le seguenti API:
- Nel backend NDK:
AIBinder_getExtension
- Nel backend Java:
android.os.IBinder.getExtension
- Nel backend CPP:
android::IBinder::getExtension
- Nel backend Rust:
binder::Binder::get_extension
Puoi trovare ulteriori informazioni su queste API nella documentazione del
Funzione getExtension
nel backend corrispondente. Un esempio di come utilizzare
disponibili in
hardware/interfaces/tests/extension/vibrator.
Principali differenze tra AIDL e HIDL
Quando utilizzi AIDL HAL o le interfacce AIDL HAL, tieni presente le differenze rispetto alla scrittura di HIDL HAL.
- La sintassi del linguaggio AIDL è più simile a Java. La sintassi HIDL è simile a quella di C++.
- Tutte le interfacce AIDL hanno stati di errore integrati. Invece di creare configurazioni personalizzate
tipi di stato, creare int di stato costanti nei file di interfaccia e utilizzare
EX_SERVICE_SPECIFIC
nei backend CPP/NDK eServiceSpecificException
nel backend Java. Vedi Errore Utilizzo. - AIDL non avvia automaticamente i pool di thread quando vengono inviati oggetti binder. Devono essere avviati manualmente (vedi thread gestione dei dispositivi).
- AIDL non viene interrotta in caso di errori di trasporto non controllati (il valore HIDL
Return
viene interrotto il giorno non selezionati). - AIDL può dichiarare un solo tipo per file.
- Gli argomenti AIDL possono essere specificati come in/out/inout oltre che come output (non sono presenti "chiamate sincrone").
- AIDL utilizza un fd come tipo primitivo invece di handle.
- HIDL utilizza le versioni principali per le modifiche incompatibili e le versioni secondarie per
modifiche compatibili. In AIDL vengono applicate modifiche compatibili con le versioni precedenti.
AIDL non ha un concetto esplicito di versioni principali; al suo posto,
i nomi dei pacchetti. Ad esempio, AIDL potrebbe utilizzare il nome del pacchetto
bluetooth2
. - AIDL non eredita la priorità in tempo reale per impostazione predefinita.
setInheritRt
deve essere utilizzata per ogni raccoglitore per abilitare l'ereditarietà della priorità in tempo reale.