AIDL per gli HAL

Android 11 introduce la possibilità di utilizzare AIDL per HAL in Android. Ciò rende possibile implementare parti di Android senza HIDL. Gli HAL di transizione utilizzano esclusivamente AIDL ove possibile (quando gli HAL a monte utilizzano HIDL, deve essere utilizzato HIDL).

Gli HAL che utilizzano AIDL per comunicare tra componenti del framework, come quelli in system.img , e componenti hardware, come quelli in vendor.img , devono utilizzare Stable AIDL. Tuttavia, per comunicare all'interno di una partizione, ad esempio da un HAL a un altro, non ci sono restrizioni sul meccanismo IPC da utilizzare.

Motivazione

AIDL esiste da più tempo di HIDL e viene utilizzato in molti altri luoghi, ad esempio tra i componenti del framework Android o nelle app. Ora che AIDL ha il supporto per la stabilità, è possibile implementare un intero stack con un unico runtime IPC. AIDL ha anche un sistema di controllo delle versioni migliore di HIDL.

  • Utilizzare un unico linguaggio IPC significa avere solo una cosa da imparare, eseguire il debug, ottimizzare e proteggere.
  • AIDL supporta il controllo delle versioni sul posto per i proprietari di un'interfaccia:
    • I proprietari possono aggiungere metodi alla fine delle interfacce o campi ai lotti. Ciò significa che è più facile modificare il codice nel corso degli anni e anche il costo anno su anno è inferiore (i tipi possono essere modificati sul posto e non sono necessarie librerie aggiuntive per ogni versione dell'interfaccia).
    • Le interfacce di estensione possono essere collegate in fase di esecuzione piuttosto che nel sistema di tipi, quindi non è necessario riassegnare le estensioni a valle alle versioni più recenti delle interfacce.
  • Un'interfaccia AIDL esistente può essere utilizzata direttamente quando il suo proprietario decide di stabilizzarla. Prima, era necessario creare un'intera copia dell'interfaccia in HIDL.

Scrittura di un'interfaccia AIDL HAL

Affinché un'interfaccia AIDL possa essere utilizzata tra il sistema e il fornitore, l'interfaccia necessita di due modifiche:

  • Ogni definizione di tipo deve essere annotata con @VintfStability .
  • La dichiarazione aidl_interface deve includere la stability: "vintf",

Solo il proprietario di un'interfaccia può apportare queste modifiche.

Inoltre, per la massima portabilità del codice e per evitare potenziali problemi come librerie aggiuntive non necessarie, disabilitare il backend CPP.

Si noti che l'uso dei backends nell'esempio di codice riportato di seguito è corretto, poiché sono presenti tre backend (Java, NDK e CPP). Il codice seguente spiega come selezionare specificamente il backend CPP, per disabilitarlo.

    aidl_interface: {
        ...
        backends: {
            cpp: {
                enabled: false,
            },
        },
    }

Trovare le interfacce HAL di AIDL

Le interfacce AIDL stabili di AOSP per HAL si trovano nelle stesse directory di base delle interfacce HIDL, nelle cartelle di aidl .

  • hardware / interfacce
  • framework / hardware / interfacce
  • sistema / hardware / interfacce

È necessario inserire le interfacce di estensione in altre sottodirectory di hardware/interfaces del vendor o hardware .

Interfacce di estensione

Android ha una serie di interfacce AOSP ufficiali con ogni versione. Quando i partner Android desiderano aggiungere funzionalità a queste interfacce, non dovrebbero modificarle direttamente perché ciò significherebbe che il loro runtime Android è incompatibile con il runtime Android AOSP. Per i dispositivi GMS, evitare di modificare queste interfacce è anche ciò che garantisce che l'immagine GSI possa continuare a funzionare.

Gli interni possono registrarsi in due modi diversi:

  • in fase di esecuzione, vedere le estensioni allegate .
  • autonomo, registrato a livello globale e in VINTF.

Tuttavia, un'estensione viene registrata, quando i componenti specifici del fornitore (ovvero non una parte di AOSP a monte) utilizzano l'interfaccia, non vi è alcuna possibilità di conflitto di unione. Tuttavia, quando vengono apportate modifiche a valle ai componenti AOSP a monte, possono verificarsi conflitti di unione e sono consigliate le seguenti strategie:

  • le aggiunte all'interfaccia possono essere trasferite ad AOSP nella prossima versione
  • Le aggiunte all'interfaccia che consentono un'ulteriore flessibilità, senza conflitti di fusione, possono essere caricate in upstream nella prossima versione

Costruire contro il runtime AIDL

AIDL ha tre diversi backend: Java, NDK, CPP. Per usare Stable AIDL, devi sempre usare la copia di sistema di libbinder in system/lib*/libbinder.so e parlare su /dev/binder . Per il codice sull'immagine del fornitore, ciò significa che libbinder (dal VNDK) non può essere utilizzato: questa libreria ha un'API C ++ instabile e interni instabili. Invece, il codice del fornitore nativo deve utilizzare il backend NDK di AIDL, collegarsi a libbinder_ndk (che è supportato dal sistema libbinder.so ) e collegare alle librerie -ndk_platform create dalle voci aidl_interface .

Nomi delle istanze del server AIDL HAL

Per convenzione, i servizi AIDL HAL hanno un nome istanza nel formato $package.$type/$instance . Ad esempio, un'istanza del vibratore HAL è registrata come android.hardware.vibrator.IVibrator/default .

Scrivere un server HAL AIDL

I server AIDL devono essere dichiarati nel manifest VINTF, ad esempio in questo modo:

    <hal format="aidl">
        <name>android.hardware.vibrator</name>
        <version>1</version>
        <fqname>IVibrator/default</fqname>
    </hal>

In caso contrario, dovrebbero registrare normalmente un servizio AIDL. Quando si eseguono test VTS, è previsto che siano disponibili tutti gli HAL AIDL dichiarati.

Scrivere un client AIDL

I client AIDL devono dichiararsi nella matrice di compatibilità, ad esempio in questo 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

Usa lo strumento hidl2aidl per convertire un'interfaccia HIDL in AIDL.

Caratteristiche di hidl2aidl :

  • Crea file .aidl basati sui file .hal per il pacchetto specificato
  • Crea regole di compilazione per il pacchetto AIDL appena creato con tutti i backend abilitati
  • Creare metodi di traduzione nei backend Java, CPP e NDK per la traduzione dai tipi HIDL ai tipi AIDL
  • Crea regole di compilazione per tradurre librerie con dipendenze richieste
  • Crea asserzioni statiche per garantire che gli enumeratori HIDL e AIDL abbiano gli stessi valori nei backend CPP e NDK

Segui questi passaggi per convertire un pacchetto di file .hal in file .aidl:

  1. system/tools/hidl/hidl2aidl lo strumento che si trova in system/tools/hidl/hidl2aidl .

    La creazione di questo strumento dall'ultima fonte fornisce l'esperienza più completa. È possibile utilizzare l'ultima versione per convertire le interfacce su rami più vecchi da versioni precedenti.

    m hidl2aidl
    
  2. Eseguire lo strumento con una directory di output seguita dal pacchetto da convertire.

    hidl2aidl -o <output directory> <package>
    

    Per esempio:

    hidl2aidl -o . android.hardware.nfc@1.2
    
  3. Leggi i file generati e risolvi eventuali problemi con la conversione.

    • conversion.log contiene eventuali problemi non gestiti da risolvere prima.
    • I file .aidl generati potrebbero contenere avvisi e suggerimenti che potrebbero richiedere un'azione. Questi commenti iniziano con // .
    • Cogli l'occasione per ripulire e apportare miglioramenti al pacchetto.
  4. Costruisci solo gli obiettivi di cui hai bisogno.

    • Disabilita i backend che non verranno utilizzati. Preferisci il backend NDK rispetto al backend CPP, vedi Scelta del runtime .
    • Rimuovi le librerie di traduzione o qualsiasi codice generato che non verrà utilizzato.

Sepolicy per gli HAL dell'AIDL

Un tipo di servizio AIDL visibile al codice del fornitore deve avere l'attributo vendor_service . Altrimenti, la configurazione sepolicy è la stessa di qualsiasi altro servizio AIDL.

    type hal_power_service, service_manager_type, vendor_service;

Per la maggior parte dei servizi definiti dalla piattaforma, è già stato aggiunto un contesto del servizio con il tipo corretto (ad esempio, android.hardware.power.IPower/default è già contrassegnato come hal_power_service ). Tuttavia, se un client framework supporta più nomi di istanza, è necessario aggiungere altri nomi di istanza nei file service_contexts specifici del dispositivo.

    android.hardware.power.IPower/custom_instance u:object_r:hal_power_service:s0

Interfacce di estensione collegate

Un'estensione può essere collegata a qualsiasi interfaccia del raccoglitore, sia che si tratti di un'interfaccia di primo livello registrata direttamente con il gestore del servizio o di una sotto-interfaccia. Quando si ottiene un'estensione, è necessario confermare che il tipo di estensione sia quello previsto. Le estensioni possono essere impostate solo dal processo che serve un raccoglitore.

Le estensioni allegate devono essere utilizzate ogni volta che un'estensione modifica la funzionalità di un HAL esistente. Quando è necessaria una funzionalità completamente nuova, non è necessario utilizzare questo meccanismo e un'interfaccia di estensione può essere registrata direttamente con il gestore del servizio. Le interfacce di estensione collegate hanno più senso quando sono collegate a sotto-interfacce, perché queste gerarchie possono essere profonde o multiistanze. L'utilizzo di un'estensione globale per rispecchiare la gerarchia dell'interfaccia del raccoglitore di un altro servizio richiederebbe un'estesa contabilità per fornire funzionalità equivalenti alle estensioni collegate direttamente.

Per impostare un'estensione nel raccoglitore, utilizza le seguenti API:

  • Nel backend NDK: AIBinder_setExtension
  • Nel back-end Java: android.os.Binder.setExtension
  • Nel backend CPP: android::Binder::setExtension

Per ottenere un'estensione su un raccoglitore, utilizza le seguenti API:

  • Nel backend NDK: AIBinder_getExtension
  • Nel back-end Java: android.os.IBinder.getExtension
  • Nel backend CPP: android::IBinder::getExtension

Puoi trovare ulteriori informazioni per queste API nella documentazione della funzione getExtension nel backend corrispondente. Un esempio di come utilizzare le estensioni può essere trovato in hardware / interfaces / test / extension / vibrator .

Principali differenze AIDL / HIDL

Quando si utilizzano gli HAL AIDL o le interfacce HAL AIDL, prestare attenzione alle differenze rispetto alla scrittura di HAL HIDL.

  • La sintassi del linguaggio AIDL è più vicina a Java. La sintassi HIDL è simile a C ++.
  • Tutte le interfacce AIDL hanno stati di errore incorporati. Invece di creare tipi di stato personalizzati, creare int di stato costanti nei file di interfaccia e utilizzare EX_SERVICE_SPECIFIC nei backend CPP / NDK e ServiceSpecificException nel backend Java.
  • AIDL non avvia automaticamente i threadpool quando vengono inviati gli oggetti binder. Devono essere avviati manualmente (vedere la gestione dei thread ).
  • AIDL non si interrompe in caso di errori di trasporto non controllati (il Return HIDL si interrompe in caso di errori non controllati).
  • AIDL può dichiarare un solo tipo per file.
  • Gli argomenti AIDL possono essere specificati come in / out / inout oltre al parametro di output (non ci sono "callback sincroni").
  • AIDL utilizza un fd come tipo primitivo invece di handle.
  • HIDL utilizza versioni principali per modifiche incompatibili e versioni secondarie per modifiche compatibili. In AIDL vengono apportate modifiche compatibili con le versioni precedenti. AIDL non ha un concetto esplicito di versioni principali; invece, questo è incorporato nei nomi dei pacchetti. Ad esempio, AIDL potrebbe utilizzare il nome del pacchetto bluetooth2 .