AIDL stabile

Android 10 aggiunge il supporto di un'interfaccia Android stabile Definition Language (AIDL), un nuovo modo per tenere traccia del programma di candidatura (API) e Application Bin Interface (ABI) fornite da AIDL interfacce. Stable AIDL funziona esattamente come AIDL, ma il sistema di compilazione monitora la compatibilità dell'interfaccia e ci sono limitazioni 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). In questo caso, la relativa API viene monitorata e gestita in base alla versione in un file accanto all'interfaccia AIDL.

AIDL strutturato e stabile a confronto

AIDL strutturato si riferisce ai tipi definiti esclusivamente in AIDL. Ad esempio, un dichiarazione "parcelable" (parcelable parcelable) 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 questo 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 trovarsi in <base_path>/com/acme/Foo.aidl, dove <base_path> può essere qualsiasi directory correlata 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 dei moduli aidl_interface utilizzati. Se una delle tue interfacce AIDL utilizza un'interfaccia o un parcelable di un altroaidl_interface, inserisci il nome qui. 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 bloccate in api_dir. A partire da Android 11, le versions sono bloccate 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 versioni successive.
  • versions_with_info: elenco di tuple, ciascuna delle quali contiene il nome di una versione bloccata e un elenco con le importazioni di versione di altri moduli aidl_interface importati da questa versione di aidl_interface. La definizione della versione V di un'interfaccia AIDL IFACE si trova in aidl_api/IFACE/V. Questo campo è stato introdotto in Android 13 e non deve essere modificato direttamente in Android.bp. Il campo viene aggiunto o aggiornato chiamando *-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 la promessa di stabilità di questa interfaccia. Sono supportati solo "vintf". Se stability non è impostato, il sistema di compilazione controlla che l'interfaccia sia compatibile con le versioni precedenti, a meno che non sia specificato unstable. L'assenza di impostazione corrisponde a un'interfaccia con stabilitá all'interno di questo contesto di compilazione (quindi tutte le cose di sistema, ad esempio le cose in system.img e le partizioni correlate, o tutte le cose del fornitore, ad esempio le cose in vendor.img e le 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 monitoraggio. A partire da Android 14, il valore predefinito è true per i backend cpp e java.
  • 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 attivati per impostazione predefinita. Se uno di questi tre backend non è necessario, deve essere è disabilitata in modo esplicito. Rust è disattivato per impostazione predefinita fino ad Android 15.
  • 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 codice aggiuntivo per raccogliere 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. Il valore predefinito è "system_current". Questo valore non deve essere impostato quando backend.java.platform_apis è true.
  • backend.java.platform_apis: il flag facoltativo che deve essere impostato su true quando le librerie generate devono essere compilate in base all'API della piattaforma piuttosto che all'SDK.

Per ogni combinazione di versioni e backend abilitati, viene creata una libreria di stub. 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. In Android 12, sono supportati anche i valori predefiniti per le enumerazioni definite dall'utente. 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

La dichiarazione di un modulo con il nome foo crea anche un target nel sistema di compilazione che puoi utilizzare per gestire l'API del modulo. Al momento della compilazione, foo-freeze-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, entrambi che rappresentano la nuova versione bloccata dell' interfaccia. foo-freeze-api aggiorna anche la proprietà versions_with_info per riflettere la versione aggiuntiva e imports per la versione. In sostanza,imports in versions_with_info viene copiato dal campo imports. Tuttavia, la versione stabile più recente è specificata in imports in versions_with_info per l' importazione, che non ha una versione esplicita. Dopo aver specificato la proprietà versions_with_info, il sistema di compilazione esegue controlli di compatibilità tra le versioni bloccate e anche tra la versione principale (ToT) e la versione bloccata più recente.

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:

  • Metodi alla fine di un'interfaccia (o metodi con nuovi metodi di serializzazione definiti esplicitamente)
  • Elementi alla fine di un parcelable (è necessario aggiungere un valore predefinito per ogni elemento)
  • Valori costanti
  • In Android 11, gli enumeratori
  • In Android 12, i campi alla fine di un'unione

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 compilazione richiede il blocco di tutte le interfacce AIDL stabili per le quali non è specificato alcun 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 a livello di livello AIDL stabile. 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. In genere, per i pacchetti solo di tipi o comuni, questa operazione è sicura perché il codice deve essere già scritto per gestire i tipi sconosciuti dalle 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 esecuzione, quando si tenta di chiamare nuovi metodi su un vecchio server, i nuovi client ricevono un errore o un'eccezione, a seconda del backend.

  • Il backend di cpp riceve ::android::UNKNOWN_TRANSACTION.
  • Il backend di ndk riceve STATUS_UNKNOWN_TRANSACTION.
  • Il backend di java riceve android.os.RemoteException con un messaggio che indica che l'API non è implementata.

Per le strategie per gestire questo problema, consulta Eseguire query sulle versioni e Utilizzare 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 devono essere specificati 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 valori costanti e gli enumeratori non riconosciuti, in quanto potrebbero essere aggiunti altri in 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

Il tentativo di inviare un'unione con un nuovo campo non va a buon fine se il destinatario è precedente e non conosce il 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.

Gestisci più versioni

Uno spazio dei nomi linker in Android può avere solo 1 versione di un aidl specifico per evitare situazioni in cui i tipi aidl generati hanno più le tue definizioni. C++ prevede la regola di singola definizione che richiede una sola definizione di ciascun simbolo.

La build di Android restituisce un errore quando un modulo dipende da della stessa libreria aidl_interface. Il modulo potrebbe dipendere direttamente o indirettamente tramite dipendenze del loro delle dipendenze. Questi errori mostrano il grafico delle dipendenze dal modulo con errori le versioni in conflitto della libreria aidl_interface. Tutte le le dipendenze devono essere aggiornate in modo da includere la stessa versione (di solito la più recente) di queste librerie.

Se la libreria dell'interfaccia viene utilizzata da molti moduli diversi, può essere utile per creare cc_defaults, java_defaults e rust_defaults per qualsiasi gruppo di e i processi che devono usare la stessa versione. Quando viene introdotta una nuova versione dell'interfaccia, i valori predefiniti possono essere aggiornati e tutti i moduli che li utilizzano vengono aggiornati insieme, garantendo che non vengano utilizzate versioni diverse dell'interfaccia.

cc_defaults {
  name: "my.aidl.my-process-group-ndk-shared",
  shared_libs: ["my.aidl-V3-ndk"],
  ...
}

cc_library {
  name: "foo",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

cc_binary {
  name: "bar",
  defaults: ["my.aidl.my-process-group-ndk-shared"],
  ...
}

Quando i moduli aidl_interface importano altri moduli aidl_interface, vengono create dipendenza aggiuntive che richiedono l'utilizzo congiunto di versioni specifiche. Questo ogni situazione può diventare difficile da gestire in presenza di aidl_interface moduli importati in più moduli aidl_interface utilizzati negli stessi processi.

aidl_interfaces_defaults può essere utilizzato per mantenere una definizione delle versioni più recenti delle dipendenze per un aidl_interface che può essere aggiornata in un unico posto e utilizzata da tutti i moduli aidl_interface che vogliono importare l'interfaccia comune.

aidl_interface_defaults {
  name: "android.popular.common-latest-defaults",
  imports: ["android.popular.common-V3"],
  ...
}

aidl_interface {
  name: "android.foo",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

aidl_interface {
  name: "android.bar",
  defaults: ["my.aidl.latest-ndk-shared"],
  ...
}

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 in fase di esecuzione per queste librerie di interfaccia non bloccate in modo che il codice possa essere scritto in base all'ultima versione non bloccata ed essere comunque utilizzato sui dispositivi di rilascio. Il comportamento compatibile con le versioni precedenti dei client è simile al comportamento esistente e, con il fallback, anche le implementazioni devono seguire questi comportamenti. Vedi Utilizzare interfacce con versione.

Flag build AIDL

Il flag che controlla questo comportamento è RELEASE_AIDL_USE_UNFROZEN definito in build/release/build_flags.bzl. true indica che la versione non bloccata dell'interfaccia viene utilizzata in fase di esecuzione e false indica che le librerie delle versioni non bloccate 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

Gli oggetti interfaccia del fornitore (VINTF) definiscono le versioni previste e quelle fornite su entrambi i lati dell'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 non bloccata, utilizza <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 viene introdotta una modifica in libvintf per modificare i file manifest in fase di compilazione in base al valore di 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 modificate da 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ù manifest o frammenti 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 sembrano sempre come l'ultima versione bloccata o precedente (ad esempio, l'uso di nuovi metodi non bloccati restituisce UNKNOWN_TRANSACTION o i nuovi campi parcelable hanno i valori predefiniti). I client del framework Android devono essere compatibili con le versioni precedenti, ma questo è un nuovo dettaglio per i client dei fornitori e i client delle interfacce di proprietà dei partner.

Modifiche all'implementazione di 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. La compatibilità con le versioni precedenti nelle implementazioni e nel codice del dispositivo è un nuovo esercizio. Consulta l'articolo Utilizzare il controllo delle versioni di archiviazione.

Le considerazioni sulla compatibilità con le versioni precedenti sono in genere le stesse per i client e i server, nonché per il codice del framework e del fornitore, ma esistono differenze sottili che devi conoscere, poiché ora stai effettivamente implementando due versioni che utilizzano lo stesso codice sorgente (la versione corrente non bloccata).

Esempio: un'interfaccia ha tre versioni bloccate. L'interfaccia è aggiornata con nuovo metodo. Il client e il servizio vengono aggiornati per utilizzare la nuova libreria versione 4. 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 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. I client potrebbero implementare due diverse versioni di un callback in base alla configurazione della release, pertanto non puoi assumere che il client invii la versione più recente e i nuovi metodi potrebbero restituire questo valore. 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 esistere o contenere i valori predefiniti quando RELEASE_AIDL_USE_UNFROZEN è false e i valori dei nuovi campi che un servizio tenta di inviare vengono eliminati al termine della procedura.

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 utilizzare i nuovi enumeratori solo con la versione in cui sono stati introdotti, e non con 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, stai implementando due versioni diverse, quindi ti consigliamo di ottenere la versione dell'interfaccia attuale. Puoi farlo recuperando la versione dell'interfaccia attuale, ad esempio this->getInterfaceVersion() o l'altro per my_ver. Per ulteriori informazioni, consulta la sezione Eseguire query sulla versione dell'interfaccia dell'oggetto remoto.

Nuove interfacce stabili VINTF

Quando viene aggiunto un nuovo pacchetto di interfacce AIDL, non esiste un'ultima versione bloccata, pertanto non è previsto alcun comportamento di riserva 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 e non puoi aggiungerlo al dispositivo in modo condizionale, puoi verificare se il servizio è dichiarato con IServiceManager::isDeclared(). Se è stato dichiarato e la registrazione non è riuscita, interrompete la procedura. Se non viene dichiarato, la registrazione potrebbe non riuscire.

Cuttlefish 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. Ha come target il livello FCM e PRODUCT_SHIPPING_API_LEVEL della release di Android del prossimo anno e deve soddisfare i requisiti di 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 verrà ottenuta con diversi rami Git che non rilevano la modifica a FCMtarget-level, al livello dell'API di produzione o a qualsiasi altro codice che ha come target la release successiva.

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 fare riferimento a un modulo della libreria stub specifico per il collegamento, non utilizzare il nome del modulo aidl_interface, ma il nome del modulo della libreria stub, ovvero ifacename-version-backend, dove

  • ifacename: nome del modulo aidl_interface
  • version è una di
    • Vversion-number per le versioni non aggiornabili
    • Vlatest-frozen-version-number + 1 per la versione di punta dell'albero (non ancora bloccata)
  • backend è uno 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 denominato foo e che la sua versione più recente sia 2, e che supporti sia NDK che C++. In questo caso, AIDL genera questi moduli:

  • In base alla versione 1
    • foo-V1-(java|cpp|ndk|ndk_platform|rust)
  • In base alla versione 2 (la versione stabile più recente)
    • foo-V2-(java|cpp|ndk|ndk_platform|rust)
  • In base alla versione di 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, che faceva riferimento alla versione ToT, 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 di 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 dell'interfaccia meta

Android 10 aggiunge diversi metodi di meta interfaccia per l'AIDL stabile.

Esegui una 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 al posto di 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é le classi generate (IFoo, IFoo.Stub e così via) sono condivise tra il client e il server (ad esempio, le classi possono trovarsi nel percorso di classe di avvio). Quando i corsi vengono condivisi, il server è collegato anche alla versione più recente dei corsi, anche se potrebbe essere stato creato con una versione precedente dell'interfaccia. Se questa metainterfaccia è implementata nella classe condivisa, 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 nell'implementazione predefinita viene invocato solo quando non è implementato nel lato remoto (perché è stato creato con una versione precedente dell'interfaccia). Poiché i valori predefiniti sono impostati a livello globale, non devono essere utilizzati da contesti potenzιακά condivisi.

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 hai già un'interfaccia AIDL e il codice che la utilizza, segui i passaggi riportati di seguito per convertire l'interfaccia in un'interfaccia AIDL stabile.

  1. Identifica tutte le dipendenze dell'interfaccia. Per ogni pacchetto di cui dipende l'interfaccia, determina se il pacchetto è definito in 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.