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 AIDLFoo
definito in un pacchettocom.acme
deve trovarsi in<base_path>/com/acme/Foo.aidl
, dove<base_path>
può essere qualsiasi directory correlata alla directory in cui si trovaAndroid.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 moduliaidl_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 12versions
: le versioni precedenti dell'interfaccia bloccate inapi_dir
. A partire da Android 11, leversions
sono bloccate inaidl_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 conversions_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 inaidl_api/IFACE/V
. Questo campo è stato introdotto in Android 13 e non deve essere modificato direttamente inAndroid.bp
. Il campo viene aggiunto o aggiornato chiamando*-update-api
o*-freeze-api
. Inoltre, viene eseguita la migrazione automatica dei campiversions
aversions_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"
. Sestability
non è impostato, il sistema di compilazione controlla che l'interfaccia sia compatibile con le versioni precedenti, a meno che non sia specificatounstable
. 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 insystem.img
e le partizioni correlate, o tutte le cose del fornitore, ad esempio le cose invendor.img
e le partizioni correlate). Sestability
è 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 backendcpp
ejava
.host_supported
: il flag facoltativo che, se impostato sutrue
, 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 sutrue
, 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 sutrue
, indica che l'interfaccia non ha subito modifiche rispetto alla versione precedente dell'interfaccia. Ciò consente più controlli in fase di build. Se impostato sufalse
significa che l'interfaccia è in di sviluppo e apporta nuove modifiche, per cui l'esecuzione difoo-freeze-api
genera nuova versione e cambia automaticamente il valore intrue
. 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 conndk_header
ecpp_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 quandobackend.java.platform_apis
ètrue
.backend.java.platform_apis
: il flag facoltativo che deve essere impostato sutrue
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 campoowner:
.AIDL_FROZEN_OWNERS="aosp test"
: la build richiede tutte le interfacce AIDL stabili con il campoowner:
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
riceveSTATUS_UNKNOWN_TRANSACTION
. - Il backend di
java
riceveandroid.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 moduloaidl_interface
version
è una diVversion-number
per le versioni non aggiornabiliVlatest-frozen-version-number + 1
per la versione di punta dell'albero (non ancora bloccata)
backend
è uno dijava
per il backend Java,cpp
per il backend C++.ndk
ondk_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 solondk
.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 diventafoo-V2-backend
foo-unstable-backend
, che faceva riferimento alla versione ToT, diventafoo-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.
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.
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).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.