Questa pagina descrive le modifiche al driver binder in Android 8, fornisce dettagli sull'utilizzo dell'IPC binder e elenca i criteri SELinux richiesti.
Modifiche al driver del binder
A partire da Android 8, il framework Android e gli HAL ora comunicano tra loro utilizzando Binder. Poiché questa comunicazione aumenta notevolmente il traffico del binder, Android 8 include diversi miglioramenti progettati per mantenere veloce l'IPC del binder. I fornitori di SoC e gli OEM devono eseguire l'unione direttamente dai branch pertinenti di android-4.4, android-4.9 e versioni successive del progetto kernel/common.
Più domini (contesti) del binder
Common-4.4 e versioni successive, incluso l'upstreamPer suddividere in modo chiaro il traffico del binder tra il codice del framework (indipendente dal dispositivo) e quello del fornitore (specifico del dispositivo), Android 8 ha introdotto il concetto di contesto del binder. Ogni contesto del binder ha il proprio nodo dispositivo e il proprio gestore del contesto (servizio). Puoi accedere al gestore del contesto solo tramite il nodo del dispositivo a cui appartiene e, quando passi un nodo del binder attraverso un determinato contesto, è accessibile da quello stesso contesto solo da un altro processo, isolando così completamente i domini l'uno dall'altro. Per informazioni dettagliate sull'utilizzo, consulta vndbinder e vndservicemanager.
Scatter-gather
Common-4.4 e versioni successive, incluso l'upstreamNelle release precedenti di Android, ogni dato in una chiamata del binder veniva copiato tre volte:
- Una volta per serializzarlo in un
Parcel
nella procedura di chiamata - Una volta nel driver del kernel, copia
Parcel
nel processo di destinazione - Una volta per deserializzare
Parcel
nel processo target
Android 8 utilizza
l'ottimizzazione
scatter-gather per ridurre il numero di copie da 3 a 1. Invece di eseguire la serializzazione dei dati in un Parcel
, i dati rimangono nella loro struttura e nel layout della memoria originali e il driver li copia immediatamente nel processo di destinazione. Una volta che i dati sono nel processo di destinazione, la struttura e il layout della memoria sono gli stessi e i dati possono essere letti senza richiedere un'altra copia.
Blocco granulare
Common-4.4 e versioni successive, incluso l'upstreamNelle release precedenti di Android, il driver binder utilizzava un blocco globale per proteggersi dall'accesso simultaneo alle strutture di dati critiche. Sebbene la competizione per il blocco fosse minima, il problema principale era che se un thread con priorità bassa otteneva il blocco e poi veniva eseguito in preemption, poteva ritardare notevolmente i thread con priorità più elevata che dovevano ottenere lo stesso blocco. Ciò ha causato un ritardo nella piattaforma.
I primi tentativi di risolvere questo problema prevedevano la disattivazione della preemption mantenendo bloccato il blocco globale. Tuttavia, si trattava più di un hack che di una vera soluzione, e alla fine è stato rifiutato dall'upstream e ignorato. I tentativi successivi si sono concentrati su come rendere il blocco più granulare, una versione di cui è in esecuzione su Pixel da gennaio 2017. Sebbene la maggior parte di queste modifiche sia stata comunicata al pubblico, nelle versioni successive sono stati apportati miglioramenti sostanziali.
Dopo aver identificato piccoli problemi nell'implementazione dei blocchi granulari, abbiamo sviluppato una soluzione migliorata con un'architettura di blocco diversa e inviato le modifiche in tutti i branch del kernel comuni. Continuiamo a testare questa implementazione su un numero elevato di dispositivi diversi. Poiché non siamo a conoscenza di problemi in sospeso, questa è l'implementazione consigliata per i dispositivi con Android 8.
Eredità della priorità in tempo reale
Common-4.4 e common-4.9 (upstream disponibile a breve)Il driver del binder ha sempre supportato una buona eredità della priorità. Poiché un numero crescente di processi in Android viene eseguito con priorità in tempo reale, in alcuni casi ora ha senso che se un thread in tempo reale effettua una chiamata al binder, anche il thread nel processo che gestisce la chiamata venga eseguito con priorità in tempo reale. Per supportare questi casi d'uso, Android 8 ora implementa l'eredità della priorità in tempo reale nel driver del binder.
Oltre all'eredità della priorità a livello di transazione, l'eredità della priorità del nodo consente a un nodo (oggetto del servizio di collegamento) di specificare una priorità minima con cui devono essere eseguite le chiamate a questo nodo. Le versioni precedenti di Android supportavano già l'eredità della priorità dei nodi con valori di buona qualità, ma Android 8 aggiunge il supporto dell'eredità dei nodi dei criteri di pianificazione in tempo reale.
Modifiche allo spazio utente
Android 8 include tutte le modifiche dello spazio utente necessarie per funzionare con il driver binder corrente nel kernel comune, con un'eccezione: l'implementazione originale per disattivare l'eredità della priorità in tempo reale per /dev/binder
utilizzava un ioctl. Lo sviluppo successivo ha spostato il controllo dell'eredità della priorità su un metodo più granulare, per modalità del binder (e non per contesto). Pertanto, ioctl non è nel ramo comune di Android, ma viene invece inviato nei nostri kernel comuni.
L'effetto di questa modifica è che l'eredità della priorità in tempo reale è disattivata per impostazione predefinita per ogni nodo. Il team di rendimento di Android ha riscontrato che è utile attivare l'eredità della priorità in tempo reale per tutti i nodi nel dominio hwbinder
. Per ottenere lo stesso effetto, scegli questa modifica nello spazio utente.
SHA per i kernel comuni
Per ottenere le modifiche necessarie al driver del binder, esegui la sincronizzazione con l'SHA appropriato:
- Common-3.18
cc8b90c121de ANDROID: binder: don't check prio permissions on restore. - Common-4.4
76b376eac7a2 ANDROID: binder: don't check prio permissions on restore. - Common-4.9
ecd972d4f9b5 ANDROID: binder: don't check prio permissions on restore.
Lavorare con l'IPC di Binder
In passato, i processi dei fornitori utilizzavano la comunicazione tra processi (IPC) del binder per comunicare. In Android 8, il nodo del dispositivo /dev/binder
diventa esclusivo per i processi del framework, il che significa che i processi del fornitore non hanno più accesso. I processi del fornitore possono accedere a /dev/hwbinder
, ma devono convertire le proprie interfacce AIDL per utilizzare HIDL. Per i fornitori che vogliono continuare a utilizzare le interfacce AIDL tra i processi del fornitore, Android supporta l'IPC Binder come descritto di seguito. In Android 10, Stable AIDL consente a tutte le procedure di utilizzare /dev/binder
, garantendo al contempo la stabilità di HIDL e /dev/hwbinder
. Per scoprire come utilizzare la versione stabile di AIDL, consulta AIDL per HAL.
vndbinder
Android 8 supporta un nuovo dominio binder da utilizzare per i servizi del fornitore, a cui si accede utilizzando /dev/vndbinder
anziché /dev/binder
. Con l'aggiunta di /dev/vndbinder
, Android ora dispone dei seguenti tre domini IPC:
Dominio IPC | Descrizione |
---|---|
/dev/binder |
IPC tra i processi del framework/dell'app con interfacce AIDL |
/dev/hwbinder |
IPC tra i processi del framework/del fornitore con interfacce HIDL
IPC tra i processi del fornitore con interfacce HIDL |
/dev/vndbinder |
IPC tra i processi del fornitore/del fornitore con interfacce AIDL |
Affinché /dev/vndbinder
venga visualizzato, assicurati che l'elemento di configurazione del kernel CONFIG_ANDROID_BINDER_DEVICES
sia impostato su "binder,hwbinder,vndbinder"
(questo è il valore predefinito negli alberi del kernel comuni di Android).
In genere, i processi del fornitore non aprono direttamente il driver del binder, ma si collegano alla libreria dello spazio utente libbinder
, che apre il driver del binder. L'aggiunta di un metodo per ::android::ProcessState()
seleziona il driver del binder per libbinder
. Le procedure del fornitore devono chiamare questo metodo prima di chiamare ProcessState,
IPCThreadState
o prima di effettuare chiamate al binder in generale. Per usarla, effettua la seguente chiamata dopo il main()
di un processo del fornitore (client e server):
ProcessState::initWithDriver("/dev/vndbinder");
vndservicemanager
In precedenza, i servizi di binder erano registrati in servicemanager
,
dove potevano essere recuperati da altri processi. In Android 8,
servicemanager
viene ora utilizzato esclusivamente dai processi di framework e app
e i processi del fornitore non possono più accedervi.
Tuttavia, i servizi dei fornitori ora possono utilizzare vndservicemanager
, una nuova istanza di servicemanager
che utilizza /dev/vndbinder
anziché /dev/binder
e che viene creata dalle stesse origini del framework servicemanager
. I processi del fornitore non devono apportare modifiche per comunicare con vndservicemanager
; quando si apre un processo del fornitore, le ricerche di servizi vanno automaticamente a vndservicemanager
.dev/vndbinder
Il file binario vndservicemanager
è incluso nei file makefile dei dispositivi predefiniti di Android.
Norme SELinux
I processi dei fornitori che vogliono utilizzare la funzionalità del binder per comunicare tra loro devono disporre di quanto segue:
- Accedi a
/dev/vndbinder
. - Il raccoglitore
{transfer, call}
si aggancia avndservicemanager
. binder_call(A, B)
per qualsiasi dominio del fornitore A che vuole chiamare il dominio del fornitore B tramite l'interfaccia del binder del fornitore.- Autorizzazione per i servizi
{add, find}
invndservicemanager
.
Per soddisfare i requisiti 1 e 2, utilizza la macro vndbinder_use()
:
vndbinder_use(some_vendor_process_domain);
Per soddisfare il requisito 3, il binder_call(A, B)
per i processi del fornitore A e B che devono comunicare tramite il binder può rimanere invariato e non deve essere rinominato.
Per soddisfare il requisito 4, devi apportare modifiche al modo in cui vengono gestiti i nomi, le etichette e le regole dei servizi.
Per informazioni dettagliate su SELinux, vedi Security-Enhanced Linux in Android. Per informazioni dettagliate su SELinux in Android 8.0, consulta SELinux per Android 8.0.
Nomi dei servizi
In precedenza, il fornitore elaborava i nomi dei servizi registrati in un
file service_contexts
e aggiungeva le regole corrispondenti per accedere
a quel file. File service_contexts
di esempio da
device/google/marlin/sepolicy
:
AtCmdFwd u:object_r:atfwd_service:s0 cneservice u:object_r:cne_service:s0 qti.ims.connectionmanagerservice u:object_r:imscm_service:s0 rcs u:object_r:radio_service:s0 uce u:object_r:uce_service:s0 vendor.qcom.PeripheralManager u:object_r:per_mgr_service:s0
In Android 8, vndservicemanager
carica invece il file
vndservice_contexts
. I servizi del fornitore di cui è in corso la migrazione a
vndservicemanager
(e che sono già nel vecchio
service_contexts
file) devono essere aggiunti al nuovo
vndservice_contexts
file.
Etichette di servizio
In precedenza, le etichette di servizio come u:object_r:atfwd_service:s0
erano definite in un file service.te
. Esempio:
type atfwd_service, service_manager_type;
In Android 8, devi modificare il tipo in
vndservice_manager_type
e spostare la regola nel
vndservice.te
file. Esempio:
type atfwd_service, vndservice_manager_type;
Regole di servicemanager
In precedenza, le regole concedevano ai domini l'accesso per aggiungere o trovare servizi da
servicemanager
. Esempio:
allow atfwd atfwd_service:service_manager find; allow some_vendor_app atfwd_service:service_manager add;
In Android 8, queste regole possono rimanere in vigore e utilizzare la stessa classe. Esempio:
allow atfwd atfwd_service:service_manager find; allow some_vendor_app atfwd_service:service_manager add;