AIDL per HAL

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 includere stability: "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:

  1. 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
    
  2. 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
    
  3. 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, come toString o equals.
  4. 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.
  5. 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.

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 e ServiceSpecificException 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.