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 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 AIDLFoo
definito in un pacchettocom.acme
deve essere impostato su<base_path>/com/acme/Foo.aidl
, dove<base_path>
potrebbe essere qualsiasi directory correlato 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 diaidl_interface
moduli utilizzati. Se uno dei tuoi Le interfacce AIDL utilizzano un'interfaccia o un particolabile da un'altraaidl_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 12versions
: le versioni precedenti dell'interfaccia bloccato inapi_dir
, a partire da Android 11,versions
è bloccato 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 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'indirizzoaidl_api/IFACE/V
. Questo campo è stato introdotto in Android 13 e non deve essere modificato direttamente inAndroid.bp
. Il campo è aggiunti o aggiornati richiamando*-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 garantire la stabilità di questa interfaccia. Sono supportati solo"vintf"
. Se il criteriostability
non viene configurato, la build verifica che l'interfaccia sia compatibile con le versioni precedenti, a meno cheunstable
è 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 insystem.img
e nelle partizioni correlate oppure ad esempio elementi invendor.img
e nelle 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 tracciamento. A partire da Android 14 è il valore predefinito ditrue
percpp
ejava
backend.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 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.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 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. L'impostazione predefinita è"system_current"
. Non deve essere impostato sebackend.java.platform_apis
ètrue
.backend.java.platform_apis
: il flag facoltativo che deve essere impostato sutrue
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
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 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 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
riceveSTATUS_UNKNOWN_TRANSACTION
. - Il backend
java
ottieneandroid.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.
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 introduci un
nuova versione dell'interfaccia, è possibile aggiornare
le impostazioni predefinite e tutti i moduli
che le usano vengono aggiornate insieme, assicurandosi che non utilizzino 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 creati
dipendenze aggiuntive che richiedono l'uso combinato 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.
È possibile utilizzare aidl_interfaces_defaults
per conservare una definizione
ultime versioni delle dipendenze per un aidl_interface
che possono essere aggiornate in
in un unico posto e utilizzata da tutti i aidl_interface
moduli 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 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, viene introdotta una modifica in libvintf
a
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 moduloaidl_interface
version
è una diVversion-number
per le versioni bloccateVlatest-frozen-version-number + 1
per versione punta d'albero (ancora da congelare)
backend
è una 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 con il nome foo e che l'ultima versione sia la 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 diventafoo-V2-backend
foo-unstable-backend
, in riferimento ai ToT la versione 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 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.
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.
Converti tutti i pacchetti nella tua interfaccia in pacchetti stabili (il 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.