AIDL stabile

Android 10 aggiunge il supporto per la stabilità di un'interfaccia Android Definition Language (AIDL), un nuovo modo per tenere traccia del programma di candidatura (API) e Application Bin Interface (ABI) fornite da AIDL interfacce. L'AIDL stabile funziona esattamente come AIDL, ma il sistema di compilazione tiene traccia compatibilità dell'interfaccia utente ed esistono limitazioni relative a ciò che puoi fare:

  • Le interfacce sono definite nel sistema di compilazione con aidl_interfaces.
  • Le interfacce possono contenere solo dati strutturati. Lotti che rappresentano i tipi preferiti vengono creati automaticamente in base alla loro definizione AIDL e di cui viene eseguito automaticamente il marshalling.
  • Le interfacce possono essere dichiarate come stabili (compatibili con le versioni precedenti). Quando questo la loro API viene tracciata e ne viene eseguito il controllo delle versioni in un file accanto a riga di comando.

AIDL strutturato e stabile a confronto

AIDL strutturato si riferisce ai tipi definiti esclusivamente in AIDL. Ad esempio, un dichiarazione "parcelable" (parcelable parcelable personalizzato) non è un AIDL strutturato. Lotti da parte e i campi definiti in AIDL sono chiamati parcelabili strutturati.

AIDL stabile richiede un AIDL strutturato in modo che il sistema di compilazione e il compilatore può capire se le modifiche apportate ai dati particellari sono compatibili con le versioni precedenti. Tuttavia, non tutte le interfacce strutturate sono stabili. Per essere stabili, un'interfaccia deve utilizzare solo tipi strutturati, oltre a quanto segue di controllo delle versioni. Al contrario, un'interfaccia non è stabile se la build del core o se unstable:true è impostato.

Definire un'interfaccia AIDL

Una definizione di aidl_interface ha il seguente aspetto:

aidl_interface {
    name: "my-aidl",
    srcs: ["srcs/aidl/**/*.aidl"],
    local_include_dir: "srcs/aidl",
    imports: ["other-aidl"],
    versions_with_info: [
        {
            version: "1",
            imports: ["other-aidl-V1"],
        },
        {
            version: "2",
            imports: ["other-aidl-V3"],
        }
    ],
    stability: "vintf",
    backend: {
        java: {
            enabled: true,
            platform_apis: true,
        },
        cpp: {
            enabled: true,
        },
        ndk: {
            enabled: true,
        },
        rust: {
            enabled: true,
        },
    },

}
  • name: il nome del modulo di interfaccia AIDL che identifica in modo univoco un AIDL.
  • srcs: l'elenco di file di origine AIDL che compongono l'interfaccia. Il percorso per un tipo AIDL Foo definito in un pacchetto com.acme deve essere impostato su <base_path>/com/acme/Foo.aidl, dove <base_path> potrebbe essere qualsiasi directory correlato alla directory in cui si trova Android.bp. Nell'esempio precedente, <base_path> è srcs/aidl.
  • local_include_dir: il percorso da cui inizia il nome del pacchetto. it corrisponde a <base_path> spiegato sopra.
  • imports: un elenco di aidl_interface moduli utilizzati. Se uno dei tuoi Le interfacce AIDL utilizzano un'interfaccia o un particolabile da un'altra aidl_interface, inserisci qui il suo nome. Può essere il nome da solo, per consultare le ultime novità o il nome con il suffisso della versione (ad esempio -V1) per fare riferimento una specifica versione. La specifica di una versione è supportata da Android 12
  • versions: le versioni precedenti dell'interfaccia bloccato in api_dir, a partire da Android 11, versions è bloccato in aidl_api/name. Se non esistono versioni bloccate di un'interfaccia, questo non deve essere specificato e non vengono eseguiti controlli di compatibilità. Questo campo è stato sostituito con versions_with_info per Android 13 e successive.
  • versions_with_info: elenco di tuple, ciascuna delle quali contiene il nome di un versione bloccata e un elenco con importazioni di versioni di altro aidl_interface moduli importati da questa versione di aidl_interface. Definizione della versione V di un'interfaccia AIDL IFACE si trova all'indirizzo aidl_api/IFACE/V. Questo campo è stato introdotto in Android 13 e non deve essere modificato direttamente in Android.bp. Il campo è aggiunti o aggiornati richiamando *-update-api o *-freeze-api. Inoltre, viene eseguita la migrazione automatica dei campi versions a versions_with_info quando un utente richiama *-update-api o *-freeze-api.
  • stability: il flag facoltativo per garantire la stabilità di questa interfaccia. Sono supportati solo "vintf". Se il criterio stability non viene configurato, la build verifica che l'interfaccia sia compatibile con le versioni precedenti, a meno che unstable è specificato. Se non viene configurato corrisponde a un'interfaccia con stabilità all'interno del contesto di compilazione (quindi per tutti gli elementi del sistema, ad esempio elementi in system.img e nelle partizioni correlate oppure ad esempio elementi in vendor.img e nelle partizioni correlate). Se stability è impostato su "vintf", ciò corrisponde a una promessa di stabilità: l'interfaccia deve rimanere stabile finché viene utilizzata.
  • gen_trace: il flag facoltativo per attivare o disattivare il tracciamento. A partire da Android 14 è il valore predefinito di true per cpp e java backend.
  • host_supported: il flag facoltativo che, se impostato su true, rende la disponibili per l'ambiente host.
  • unstable: il flag facoltativo utilizzato per indicare che l'interfaccia non devono essere stabili. Se questo criterio viene impostato su true, il sistema di compilazione non crea il dump dell'API per l'interfaccia né ne richiede l'aggiornamento.
  • frozen: il flag facoltativo che, se impostato su true, indica che l'interfaccia non ha subito modifiche rispetto alla versione precedente dell'interfaccia. Ciò consente più controlli in fase di build. Se impostato su false significa che l'interfaccia è in di sviluppo e apporta nuove modifiche, per cui l'esecuzione di foo-freeze-api genera nuova versione e cambia automaticamente il valore in true. Introdotta in Android 14.
  • backend.<type>.enabled: questi flag attivano/disattivano ogni backend che per cui il compilatore AIDL genera il codice. Esistono quattro backend supportate: Java, C++, NDK e Rust. I backend Java, C++ e NDK sono abilitati per impostazione predefinita. Se uno di questi tre backend non è necessario, deve essere è disabilitata in modo esplicito. Ruggine è disattivata per impostazione predefinita fino ad Android 15 (AOSP sperimentale).
  • backend.<type>.apex_available: l'elenco di nomi APEX generati per i quali è disponibile la libreria stub.
  • backend.[cpp|java].gen_log: il flag facoltativo che controlla se generare un codice aggiuntivo per la raccolta di informazioni sulla transazione.
  • backend.[cpp|java].vndk.enabled: il flag facoltativo per rendere questa interfaccia faceva parte di VNDK. Il valore predefinito è false.
  • backend.[cpp|ndk].additional_shared_libraries: introduzione in Android 14, questo flag aggiunge dipendenze librerie native. Questo flag è utile con ndk_header e cpp_header.
  • backend.java.sdk_version: il flag facoltativo per specificare la versione dell'SDK su cui viene creata la libreria stub Java. L'impostazione predefinita è "system_current". Non deve essere impostato se backend.java.platform_apis è true.
  • backend.java.platform_apis: il flag facoltativo che deve essere impostato su true quando le librerie generate devono essere create nell'API della piattaforma anziché con l'SDK.

Per ogni combinazione delle versioni e dei backend abilitati, uno stub viene creata. Per fare riferimento alla versione specifica della libreria stub per un backend specifico, consulta Regole di denominazione dei moduli.

Scrittura di file AIDL

Le interfacce in un AIDL stabile sono simili alle interfacce tradizionali, con ad eccezione del fatto che non è consentito utilizzare pacchetti non strutturati (perché non sono stabili. consulta Struttura e confronto stabile AIDL. La differenza principale nell'AIDL stabile è come le particellari sono definite. In precedenza, i pacchetti venivano dichiarati in inoltro, nel AIDL stabile (e quindi strutturato), i campi catastali e le variabili sono definiti in modo esplicito.

// in a file like 'some/package/Thing.aidl'
package some.package;

parcelable SubThing {
    String a = "foo";
    int b;
}

È supportato un valore predefinito (ma non obbligatorio) per boolean, char, float, double, byte, int, long e String. Su Android 12, anche i valori predefiniti per le enumerazioni definite dall'utente sono supportati. Quando non viene specificato un valore predefinito, viene utilizzato un valore pari a 0 o vuoto. Le enumerazioni senza un valore predefinito vengono inizializzate a 0 anche se è presente senza enumeratore zero.

Utilizzare le librerie stub

Dopo aver aggiunto le librerie stub come dipendenza al modulo, puoi includerle nei tuoi file. Di seguito sono riportati alcuni esempi di librerie stub sistema di compilazione (puoi utilizzare Android.mk anche per le definizioni dei moduli legacy):

cc_... {
    name: ...,
    shared_libs: ["my-module-name-cpp"],
    ...
}
# or
java_... {
    name: ...,
    // can also be shared_libs if your preference is to load a library and share
    // it among multiple users or if you only need access to constants
    static_libs: ["my-module-name-java"],
    ...
}
# or
rust_... {
    name: ...,
    rustlibs: ["my-module-name-rust"],
    ...
}

Esempio in C++:

#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
    // use just like traditional AIDL

Esempio in Java:

import some.package.IFoo;
import some.package.Thing;
...
    // use just like traditional AIDL

Esempio in Rust:

use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
    // use just like traditional AIDL

Interfacce di controllo delle versioni

Anche la dichiarazione di un modulo con il nome foo crea una destinazione nel sistema di compilazione che puoi utilizzare per gestire l'API del modulo. Una volta creato, foo-free-api aggiunge una nuova definizione dell'API in api_dir o aidl_api/name, a seconda della versione di Android, e aggiunge un file .hash, che rappresentano entrambi la versione appena bloccata dell' a riga di comando. foo-ze-api aggiorna anche la proprietà versions_with_info per riflettere la versione aggiuntiva e imports per la versione. In pratica, imports in versions_with_info è stato copiato dal campo imports. Ma l'ultima versione stabile è specificata in imports in versions_with_info per , che non ha una versione esplicita. Dopo aver specificato la proprietà versions_with_info, il sistema di compilazione viene eseguito controlli di compatibilità tra versioni bloccate e Top of Tree (ToT) e l'ultima versione bloccata.

Inoltre, devi gestire la definizione dell'API della versione ToT. Ogni volta che un'API viene aggiornato, esegui foo-update-api per aggiornare aidl_api/name/current che contiene la definizione API della versione ToT.

Per mantenere la stabilità di un'interfaccia, i proprietari possono aggiungere nuovi elementi:

  • I metodi alla fine di un'interfaccia (o metodi con nuovi seriali)
  • Elementi alla fine di una particella (richiede l'aggiunta di un valore predefinito per ogni )
  • Valori costanti
  • In Android 11, gli enumeratori
  • In Android 12, i campi alla fine di un sindacato

Non sono consentite altre azioni e nessun altro può modificare un'interfaccia (altrimenti rischiano di entrare in conflitto con le modifiche apportate da un proprietario).

Per verificare che tutte le interfacce siano bloccate per il rilascio, puoi creare con il seguente insieme di variabili di ambiente:

  • AIDL_FROZEN_REL=true m ...: la build richiede tutte le interfacce AIDL stabili per bloccati per i quali non è specificato un campo owner:.
  • AIDL_FROZEN_OWNERS="aosp test": la build richiede tutte le interfacce AIDL stabili con il campo owner: specificato come "aosp" o "test".

Stabilità delle importazioni

L'aggiornamento delle versioni delle importazioni per le versioni bloccate di un'interfaccia compatibile con le versioni precedenti allo livello Stabile AIDL. Tuttavia, l'aggiornamento richiede aggiornare tutti i server e i client che utilizzano una versione precedente dell'interfaccia, e alcune app potrebbero essere confuse quando si combinano versioni diverse di tipi. Generalmente, per i pacchetti comuni o solo di tipo, questo è sicuro perché il codice deve sia già scritta per gestire i tipi sconosciuti delle transazioni IPC.

Nella piattaforma Android, il codice android.hardware.graphics.common è di questo tipo di upgrade della versione.

Utilizza interfacce con controllo delle versioni

Metodi di interfaccia

In fase di runtime, durante il tentativo di chiamare nuovi metodi su un vecchio server, i nuovi client ricevono un errore o un'eccezione, a seconda del backend.

  • Il backend cpp riceve ::android::UNKNOWN_TRANSACTION.
  • Il backend ndk riceve STATUS_UNKNOWN_TRANSACTION.
  • Il backend java ottiene android.os.RemoteException con un messaggio che dice L'API non è implementata.

Per conoscere le strategie per gestire questo problema, consulta delle versioni di query e utilizzando i valori predefiniti.

Lotti da parte

Quando vengono aggiunti nuovi campi ai pacchetti, i vecchi client e server li eliminano. Quando i nuovi client e server ricevono oggetti parcelable meno recenti, vengono applicati i valori predefiniti per i nuovi vengono compilati automaticamente. Ciò significa che i valori predefiniti specificato per tutti i nuovi campi in un "parcelable".

I client non devono aspettarsi che i server utilizzino i nuovi campi a meno che non conoscano il server web sta implementando la versione con il campo definito (vedi versioni con query).

Enum e costanti

Analogamente, i client e i server devono rifiutare o ignorare i messaggi non riconosciuti i valori costanti e gli enumeratori, poiché è possibile aggiungerne altri per il futuro. Ad esempio, un server non deve interrompersi quando riceve un enumeratore di cui non è a conoscenza. Il server dovrebbe ignorare enumeratore o restituire qualcosa in modo che il client sappia che non è supportato per questa implementazione.

Sindacati

L'invio di un Union con un nuovo campo ha esito negativo se il destinatario è vecchio e non conosce questo campo. L'implementazione non vedrà mai l'unione il nuovo campo. L'errore viene ignorato se si tratta di un transazione di sola andata; In caso contrario, l'errore è BAD_VALUE(per C++ o NDK ) o IllegalArgumentException(per il backend Java). L'errore è ricevuto se il client invia un insieme di join al nuovo campo a un vecchio o quando si tratta di un vecchio client che riceve l'unione da un nuovo server.

Sviluppo basato su flag

Le interfacce in fase di sviluppo (non bloccate) non possono essere utilizzate sui dispositivi di rilascio perché non è garantita la compatibilità con le versioni precedenti.

AIDL supporta il fallback di runtime per queste librerie di interfacce non bloccate in ordine che il codice venga scritto sull'ultima versione sbloccata affinché possa continuare a essere utilizzato sui dispositivi di rilascio. Il comportamento compatibile con le versioni precedenti dei client è simile il comportamento esistente e, con i contenuti di riserva, anche le implementazioni devono seguire questi comportamenti. Consulta Utilizza le interfacce con controllo delle versioni.

Flag build AIDL

Il flag che controlla questo comportamento è RELEASE_AIDL_USE_UNFROZEN definita in build/release/build_flags.bzl. true indica la versione sbloccata di l'interfaccia viene utilizzata in fase di esecuzione e false indica le librerie del le versioni sbloccate si comportano tutte come la loro ultima versione bloccata. Puoi eseguire l'override del flag su true per sviluppo locale, ma devi ripristinare false prima del rilascio. Tipicamente lo sviluppo viene eseguito con una configurazione con il flag impostato su true.

Matrice di compatibilità e manifest

Definizione degli oggetti dell'interfaccia del fornitore (oggetti VINTF) le versioni previste e le versioni fornite ai lati l'interfaccia del fornitore.

La maggior parte dei dispositivi non Seppia ha come target la matrice di compatibilità più recente solo dopo che le interfacce sono bloccate, quindi non c'è differenza nel librerie basate su RELEASE_AIDL_USE_UNFROZEN.

Matrici

Le interfacce di proprietà dei partner vengono aggiunte a prodotti o dispositivi specifici matrici di compatibilità target del dispositivo durante lo sviluppo. Quindi, quando viene aggiunta una nuova versione non bloccata di un'interfaccia a una matrice di compatibilità le versioni bloccate precedenti devono rimanere RELEASE_AIDL_USE_UNFROZEN=false. Puoi gestire questa situazione utilizzando diversi file di matrice di compatibilità per diversi RELEASE_AIDL_USE_UNFROZEN configurazioni o autorizzare entrambe le versioni in un unico file della matrice di compatibilità usato in tutte le configurazioni.

Ad esempio, quando aggiungi una versione 4 sbloccata, usa <version>3-4</version>.

Quando la versione 4 è bloccata, puoi rimuovere la versione 3 dalla matrice di compatibilità perché la versione bloccata 4 viene utilizzata quando RELEASE_AIDL_USE_UNFROZEN viene false.

Manifest

In Android 15 (AOSP sperimentale), viene introdotta una modifica in libvintf alle i file manifest al momento della creazione in base al valore RELEASE_AIDL_USE_UNFROZEN.

I file manifest e i frammenti di manifest dichiarano la versione di un'interfaccia implementato da un servizio. Quando utilizzi l'ultima versione sbloccata di un'interfaccia, il file manifest deve essere aggiornato per riflettere la nuova versione. Quando RELEASE_AIDL_USE_UNFROZEN=false le voci del file manifest vengono regolate in base a libvintf per riflettere la modifica nella libreria AIDL generata. Versione è stato modificato dalla versione sbloccata, N, a l'ultima versione bloccata N - 1. Pertanto, gli utenti non devono gestire più o frammenti di manifest per ciascuno dei loro servizi.

Modifiche client HAL

Il codice client HAL deve essere compatibile con le versioni precedenti di tutti i blocchi supportati in precedenza completamente gestita. Quando RELEASE_AIDL_USE_UNFROZEN è false, i servizi vengono sempre visualizzati come l'ultima versione bloccata o una precedente (ad esempio, chiamando il nuovo metodi restituisce UNKNOWN_TRANSACTION o i nuovi campi parcelable hanno i loro valori predefiniti). I client del framework Android devono tornare indietro compatibile con altre versioni precedenti, ma questo è un nuovo dettaglio per clienti di fornitori e clienti di interfacce di proprietà dei partner.

Modifiche all'implementazione dell'HAL

La principale differenza tra lo sviluppo HAL e quello basato su flag è la requisito che le implementazioni HAL siano compatibili con le versioni precedenti versione bloccata affinché funzioni quando RELEASE_AIDL_USE_UNFROZEN è false. Considerare la compatibilità con le versioni precedenti nelle implementazioni e nel codice del dispositivo è una nuova allenamento. Consulta l'articolo Utilizzare il controllo delle versioni di archiviazione.

Le considerazioni sulla compatibilità con le versioni precedenti sono generalmente le stesse per sia per il codice del framework che per il codice del fornitore, ma sottili differenze di cui devi essere a conoscenza, dato che ora puoi implementando due versioni che usano lo stesso codice sorgente (quello attuale, ).

Esempio: un'interfaccia ha tre versioni bloccate. L'interfaccia è aggiornata con nuovo metodo. Il client e il servizio vengono entrambi aggiornati per utilizzare la nuova versione 4 libreria. Poiché la libreria V4 si basa su una versione non bloccata del si comporta come l'ultima versione bloccata, la versione 3, RELEASE_AIDL_USE_UNFROZEN è false e impedisce l'utilizzo del nuovo metodo.

Quando l'interfaccia è bloccata, tutti i valori di RELEASE_AIDL_USE_UNFROZEN utilizzano questo la versione bloccata e il codice che gestisce la compatibilità con le versioni precedenti può essere rimosso.

Quando chiami metodi sui callback, devi gestire agevolmente il caso quando Viene restituito UNKNOWN_TRANSACTION. È possibile che i clienti implementino di un callback in base alla configurazione della release, quindi non puoi supponiamo che il client invii la versione più recente e che potrebbero essere restituiti nuovi metodi questo. Questo è simile al modo in cui i client AIDL stabili mantengono le versioni precedenti la compatibilità con i server descritta nella sezione Utilizzare il controllo delle versioni di archiviazione.

// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
    mMyCallback = cb;
    // Get the version of the callback for later when we call methods on it
    auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
    return status;
}

// Example of using the callback later
void NotifyCallbackLater() {
  // From the latest frozen version (V2)
  mMyCallback->foo();
  // Call this method from the unfrozen V3 only if the callback is at least V3
  if (mMyCallbackVersion >= 3) {
    mMyCallback->bar();
  }
}

I nuovi campi nei tipi esistenti (parcelable, enum, union) potrebbero non esistono o contengono i loro valori predefiniti quando RELEASE_AIDL_USE_UNFROZEN è false e i valori dei nuovi campi che un servizio tenta di inviare vengono eliminati per uscire dal processo.

Non è possibile inviare nuovi tipi aggiunti a questa versione sbloccata o ricevuto attraverso l'interfaccia.

L'implementazione non riceve mai una chiamata per nuovi metodi da nessun client quando RELEASE_AIDL_USE_UNFROZEN è false.

Fai attenzione a usare i nuovi enumeratori solo con la versione in cui sono introdotti, e non la versione precedente.

Normalmente, utilizzi foo->getInterfaceVersion() per vedere di quale versione del telecomando dell'interfaccia utente. Tuttavia, con il supporto del controllo delle versioni basato su flag, implementare due versioni diverse, quindi potresti voler ottenere la versione l'interfaccia corrente. Puoi farlo recuperando la versione dell'interfaccia attuale, ad esempio this->getInterfaceVersion() o l'altro per my_ver. Consulta Esecuzione di query sulla versione dell'interfaccia del telecomando oggetto per ulteriori informazioni.

Nuove interfacce stabili VINTF

Quando viene aggiunto un nuovo pacchetto di interfaccia AIDL, non esiste l'ultima versione bloccata, quindi non c'è alcun comportamento a cui ricorrere quando RELEASE_AIDL_USE_UNFROZEN è false. Non utilizzare queste interfacce. Quando il valore di RELEASE_AIDL_USE_UNFROZEN è false, Service Manager non consentirà al servizio di registrare l'interfaccia e i clienti non lo troveranno.

Puoi aggiungere i servizi in modo condizionale in base al valore del parametro RELEASE_AIDL_USE_UNFROZEN flag nel file makefile del dispositivo:

ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
    android.hardware.health.storage-service
endif

Se il servizio fa parte di un processo più grande, quindi non puoi aggiungerlo al dispositivo in modo condizionale, puoi controllare se il servizio viene dichiarato IServiceManager::isDeclared(). Se viene dichiarato e la registrazione non va a buon fine, interrompere la procedura. Se non viene dichiarato, la registrazione potrebbe non riuscire.

Seppia come strumento di sviluppo

Ogni anno, dopo il congelamento di VINTF, modifichiamo la compatibilità del framework matrix (FCM) target-level e PRODUCT_SHIPPING_API_LEVEL di Seppia in modo che riflettano il lancio dei dispositivi con la release del prossimo anno. Adeguiamo target-level e PRODUCT_SHIPPING_API_LEVEL per assicurarci che ci siano dispositivo che viene testato e che soddisfa i nuovi requisiti per la .

Quando RELEASE_AIDL_USE_UNFROZEN è true, la seppia è utilizzati per lo sviluppo delle future release di Android. Scegli come target Android del prossimo anno livello FCM della release e PRODUCT_SHIPPING_API_LEVEL, pertanto deve soddisfare i requisiti software del fornitore (VSR) della release successiva.

Quando il valore di RELEASE_AIDL_USE_UNFROZEN è false, la seppia ha il valore precedente target-level e PRODUCT_SHIPPING_API_LEVEL per riflettere un dispositivo di sgancio. In Android 14 e versioni precedenti, questa differenziazione sarebbe ottenuta con diversi rami Git che non rilevano il passaggio a FCM target-level, a livello di API di spedizione o a qualsiasi altro codice che ha come target il successivo .

Regole di denominazione dei moduli

In Android 11, per ogni combinazione di versioni e abilitati i backend, viene creato automaticamente un modulo della libreria stub. Per referral a un modulo specifico della libreria stub per il collegamento, non utilizzare il nome del modulo aidl_interface, ma il nome del modulo della libreria stub, che è ifacename-version-backend, dove

  • ifacename: nome del modulo aidl_interface
  • version è una di
    • Vversion-number per le versioni bloccate
    • Vlatest-frozen-version-number + 1 per versione punta d'albero (ancora da congelare)
  • backend è una di
    • java per il backend Java,
    • cpp per il backend C++,
    • ndk o ndk_platform per il backend NDK. La prima riguarda le app, mentre mentre la seconda riguarda l'utilizzo della piattaforma fino ad Android 13. Nella Android 13 e versioni successive. Usa solo ndk.
    • rust per il backend Rust.

Supponiamo che esista un modulo con il nome foo e che l'ultima versione sia 2. e supporta sia NDK che C++. In questo caso, AIDL genera i seguenti moduli:

  • In base alla versione 1
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • Basata sulla versione 2 (l'ultima versione stabile)
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • In base alla versione ToT
    • foo-V3-(java|cpp|ndk|ndk_platform|rust)

Rispetto ad Android 11:

  • foo-backend, in riferimento alla versione stabile più recente la versione diventa foo-V2-backend
  • foo-unstable-backend, in riferimento ai ToT la versione diventa foo-V3-backend

I nomi dei file di output sono sempre gli stessi dei nomi dei moduli.

  • In base alla versione 1: foo-V1-(cpp|ndk|ndk_platform|rust).so
  • In base alla versione 2: foo-V2-(cpp|ndk|ndk_platform|rust).so
  • In base alla versione ToT: foo-V3-(cpp|ndk|ndk_platform|rust).so

Tieni presente che il compilatore AIDL non crea né un modulo di versione unstable, o un modulo non sottoposto al controllo delle versioni per un'interfaccia AIDL stabile. A partire da Android 12, il nome del modulo generato da un stabile AIDL include sempre la sua versione.

Nuovi metodi della meta-interfaccia

In Android 10 sono stati aggiunti diversi metodi di meta-interfaccia per un AIDL stabile.

Query sulla versione dell'interfaccia dell'oggetto remoto

I client possono eseguire query sulla versione e sull'hash dell'interfaccia che l'oggetto remoto implementa e confronta i valori restituiti con i valori dell'interfaccia usato dal client.

Esempio con il backend cpp:

sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();

Esempio con il backend ndk (e ndk_platform):

IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
  // the remote side is using an older interface
}

std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);

Esempio con il backend java:

IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
  // the remote side is using an older interface
}

String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();

Per il linguaggio Java, il lato remoto DEVE implementare getInterfaceVersion() e getInterfaceHash() come segue (viene utilizzato super anziché IFoo per evitare gli errori di copia e incolla. L'annotazione @SuppressWarnings("static") potrebbe essere necessaria per disattivare gli avvisi, a seconda della configurazione di javac):

class MyFoo extends IFoo.Stub {
    @Override
    public final int getInterfaceVersion() { return super.VERSION; }

    @Override
    public final String getInterfaceHash() { return super.HASH; }
}

Questo perché i corsi generati (IFoo, IFoo.Stub e così via) vengono condivisi tra client e server (ad esempio, le classi possono essere nella fase di avvio classpath). Quando i corsi sono condivisi, il server viene collegato anche la versione più recente dei corsi, anche se potrebbe essere stata creata con una completamente gestita dell'interfaccia. Se questa meta-interfaccia è implementata nella , restituisce sempre la versione più recente. Tuttavia, implementando il metodo come sopra, il numero di versione dell'interfaccia è incorporato nel codice del server (perché IFoo.VERSION è una static final int allineata quando viene fatto riferimento) quindi il metodo può restituire la versione esatta con cui è stato creato il server.

Gestire le interfacce meno recenti

È possibile che un client venga aggiornato con la versione più recente di un AIDL ma il server utilizza la vecchia interfaccia AIDL. In questi casi, la chiamata di un metodo su un'interfaccia precedente restituisce UNKNOWN_TRANSACTION.

Con AIDL stabile, i client hanno un maggiore controllo. Sul lato client, puoi impostare un'implementazione predefinita a un'interfaccia AIDL. Un metodo nella definizione predefinita viene richiamata solo quando il metodo non è implementato nella (perché è stato creato con una versione meno recente dell'interfaccia). Dal giorno i valori predefiniti sono impostati a livello globale, non possono essere utilizzati da i contesti.

Esempio in C++ in Android 13 e versioni successive:

class MyDefault : public IFooDefault {
  Status anAddedMethod(...) {
   // do something default
  }
};

// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());

foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
                         // remote side is not implementing it

Esempio in Java:

IFoo.Stub.setDefaultImpl(new IFoo.Default() {
    @Override
    public xxx anAddedMethod(...)  throws RemoteException {
        // do something default
    }
}); // once per an interface in a process

foo.anAddedMethod(...);

Non è necessario fornire l'implementazione predefinita di tutti i metodi in un AIDL a riga di comando. Metodi garantiti per l'implementazione sul lato remoto (perché hai la certezza che il telecomando viene costruito quando i metodi erano nella descrizione dell'interfaccia AIDL) non devono essere sostituite nel valore predefinito impl .

Converti l'AIDL esistente in AIDL strutturato o stabile

Se disponi di un'interfaccia AIDL esistente e del codice che la utilizza, usa quanto segue passaggi per convertire l'interfaccia in un'interfaccia AIDL stabile.

  1. Identifica tutte le dipendenze dell'interfaccia. Per ogni pacco dipende dal modello, determina se il pacchetto è definito in un AIDL stabile. Se non definito, il pacchetto deve essere convertito.

  2. Converti tutti i pacchetti nella tua interfaccia in pacchetti stabili (il i file dell'interfaccia possono rimanere invariati). Per farlo che esprimono la propria struttura direttamente nei file AIDL. I corsi di gestione devono essere riscritte per utilizzare questi nuovi tipi. Questa operazione può essere eseguita prima di creare Pacchetto aidl_interface (sotto).

  3. Crea un pacchetto aidl_interface (come descritto sopra) che contenga il parametro il nome del modulo, le sue dipendenze e qualsiasi altra informazione necessaria. Per renderlo stabilizzato (non solo strutturato), deve anche essere sottoposto al controllo delle versioni. Per ulteriori informazioni, consulta la sezione Interfacce di controllo delle versioni.