Utilizzo di Raccoglitore IPC

Questa pagina descrive le modifiche al driver del binder in Android 8, fornisce dettagli sull'utilizzo dell'IPC del binder ed elenca i criteri SELinux richiesti.

Modifiche al driver del raccoglitore

A partire da Android 8, il framework Android e gli HAL ora comunicano tra loro tramite Binder. Poiché questa comunicazione aumenta notevolmente il traffico del raccoglitore, Android 8 include diversi miglioramenti progettati per mantenere veloce l'IPC del raccoglitore. I fornitori di SoC e gli OEM dovrebbero fondersi direttamente dai rami pertinenti di Android-4.4, Android-4.9 e versioni successive del kernel/progetto comune .

Più domini binder (contesti)

Common-4.4 e versioni successive, incluso l'upstream

Per suddividere in modo netto il traffico del raccoglitore tra codice del framework (indipendente dal dispositivo) e del fornitore (specifico del dispositivo), Android 8 ha introdotto il concetto di contesto del raccoglitore . Ogni contesto di bind ha il proprio nodo di dispositivo e il proprio gestore di contesto (servizio). È possibile accedere al gestore di contesto solo tramite il nodo del dispositivo a cui appartiene e, quando si passa un nodo 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 i dettagli sull'utilizzo, vedere vndbinder e vndservicemanager .

Scatter-raccogli

Common-4.4 e versioni successive, incluso l'upstream

Nelle versioni precedenti di Android, ogni pezzo di dati in una chiamata al raccoglitore veniva copiato tre volte:

  • Una volta per serializzarlo in un Parcel nel processo di chiamata
  • Una volta nel driver del kernel per copiare il Parcel nel processo di destinazione
  • Una volta per deserializzare il Parcel nel processo di destinazione

Android 8 utilizza l'ottimizzazione scatter-gather per ridurre il numero di copie da 3 a 1. Invece di serializzare prima i dati in un Parcel , i dati rimangono nella struttura originale e nel layout di memoria e il driver li copia immediatamente nel processo di destinazione. Dopo 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.

Bloccaggio a grana fine

Common-4.4 e versioni successive, incluso l'upstream

Nelle precedenti versioni di Android, il driver del raccoglitore utilizzava un blocco globale per proteggere dall'accesso simultaneo a strutture di dati critiche. Sebbene la contesa per il blocco fosse minima, il problema principale era che se un thread con priorità bassa otteneva il blocco e poi veniva preceduto, poteva ritardare seriamente i thread con priorità più alta che necessitavano di ottenere lo stesso blocco. Ciò ha causato jank nella piattaforma.

I tentativi iniziali per risolvere questo problema hanno comportato la disabilitazione della prelazione mantenendo il blocco globale. Tuttavia, questo era più un hack che una vera soluzione, e alla fine è stato rifiutato dall'upstream e scartato. I tentativi successivi si sono concentrati sul rendere il blocco più granulare, una versione del quale è in esecuzione sui dispositivi Pixel da gennaio 2017. Sebbene la maggior parte di tali modifiche sia stata resa pubblica, nelle versioni successive sono stati apportati miglioramenti sostanziali.

Dopo aver identificato piccoli problemi nell'implementazione del blocco a grana fine, abbiamo ideato una soluzione migliorata con un'architettura di blocco diversa e abbiamo inviato le modifiche in tutti i rami del kernel comuni. Continuiamo a testare questa implementazione su un gran numero di dispositivi diversi; poiché non siamo a conoscenza di eventuali problemi in sospeso, questa è l'implementazione consigliata per i dispositivi forniti con Android 8.

Ereditarietà delle priorità in tempo reale

Common-4.4 e common-4.9 (a monte in arrivo)

Il driver del raccoglitore ha sempre supportato l'ereditarietà della priorità piacevole. 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 raccoglitore, anche il thread nel processo che gestisce quella chiamata viene eseguito con priorità in tempo reale . Per supportare questi casi d'uso, Android 8 ora implementa l'ereditarietà della priorità in tempo reale nel driver del raccoglitore.

Oltre all'ereditarietà della priorità a livello di transazione, l'ereditarietà della priorità del nodo consente a un nodo (oggetto servizio binder) di specificare una priorità minima alla quale devono essere eseguite le chiamate in questo nodo. Le versioni precedenti di Android supportavano già l'ereditarietà della priorità del nodo con valori gradevoli, ma Android 8 aggiunge il supporto per l'ereditarietà del nodo delle politiche di pianificazione in tempo reale.

Modifiche dello spazio utente

Android 8 include tutte le modifiche allo spazio utente necessarie per funzionare con il driver del binder corrente nel kernel comune con un'eccezione: l'implementazione originale per disabilitare l'ereditarietà della priorità in tempo reale per /dev/binder utilizzava un ioctl . Lo sviluppo successivo ha cambiato il controllo dell'ereditarietà della priorità in un metodo più granulare che è per modalità raccoglitore (e non per contesto). Pertanto, ioctl non si trova nel ramo comune di Android ed è invece inviato nei nostri kernel comuni .

L'effetto di questa modifica è che l'ereditarietà della priorità in tempo reale è disabilitata per impostazione predefinita per ogni nodo. Il team delle prestazioni di Android ha ritenuto vantaggioso abilitare l'ereditarietà della priorità in tempo reale per tutti i nodi nel dominio hwbinder . Per ottenere lo stesso effetto, scegli con cura questo cambiamento nello spazio utente.

SHA per kernel comuni

Per ottenere le modifiche necessarie al driver del raccoglitore, eseguire la sincronizzazione con lo SHA appropriato:

  • Comune-3.18
    cc8b90c121de ANDROID: raccoglitore: non controllare le autorizzazioni prio al ripristino.
  • Comune-4.4
    76b376eac7a2 ANDROID: raccoglitore: non controllare le autorizzazioni prio al ripristino.
  • Comune-4.9
    ecd972d4f9b5 ANDROID: raccoglitore: non controllare le autorizzazioni prio al ripristino.

Utilizzo del raccoglitore IPC

Storicamente, i processi dei fornitori hanno utilizzato la comunicazione interprocesso (IPC) del raccoglitore 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 ad esso. I processi del fornitore possono accedere a /dev/hwbinder , ma devono convertire le loro interfacce AIDL per utilizzare HIDL. Per i fornitori che desiderano continuare a utilizzare le interfacce AIDL tra i processi dei fornitori, Android supporta l'IPC del raccoglitore come descritto di seguito. In Android 10, Stable AIDL consente a tutti i processi di utilizzare /dev/binder risolvendo anche per le garanzie di stabilità HIDL e /dev/hwbinder risolto. Per informazioni su come utilizzare Stable AIDL, vedere AIDL per HAL .

vndbinder

Android 8 supporta un nuovo dominio binder per l'utilizzo da parte dei servizi del fornitore, accessibile tramite /dev/vndbinder anziché /dev/binder . Con l'aggiunta di /dev/vndbinder , Android ha ora i seguenti tre domini IPC:

Dominio IPC Descrizione
/dev/binder IPC tra processi framework/app con interfacce AIDL
/dev/hwbinder IPC tra processi framework/vendor con interfacce HIDL
IPC tra processi fornitore con interfacce HIDL
/dev/vndbinder IPC tra processi fornitore/fornitore con interfacce AIDL

Affinché venga visualizzato /dev/vndbinder , assicurarsi che l'elemento di configurazione del kernel CONFIG_ANDROID_BINDER_DEVICES sia impostato su "binder,hwbinder,vndbinder" (questa è l'impostazione predefinita negli alberi del kernel comuni di Android).

Normalmente, i processi del fornitore non aprono direttamente il driver del raccoglitore e si collegano invece alla libreria dello spazio utente libbinder , che apre il driver del raccoglitore. L'aggiunta di un metodo per ::android::ProcessState() seleziona il driver del raccoglitore per libbinder . I processi del fornitore devono chiamare questo metodo prima di chiamare ProcessState, IPCThreadState o prima di effettuare qualsiasi chiamata di binder in generale. Per utilizzare, inserire la seguente chiamata dopo il main() di un processo fornitore (client e server):

ProcessState::initWithDriver("/dev/vndbinder");

vndservicemanager

In precedenza, i servizi binder venivano registrati con servicemanager , dove potevano essere recuperati da altri processi. In Android 8, servicemanager è ora utilizzato esclusivamente dal framework e dai processi delle app e i processi del fornitore non possono più accedervi.

Tuttavia, i servizi del fornitore possono ora utilizzare vndservicemanager , una nuova istanza di servicemanager che utilizza /dev/vndbinder invece di /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 un processo del fornitore apre / dev/vndbinder , le ricerche dei servizi passano automaticamente a vndservicemanager .

Il binario vndservicemanager è incluso nei makefile del dispositivo predefinito di Android.

Politica di SELinux

I processi fornitore che desiderano utilizzare la funzionalità raccoglitore per comunicare tra loro necessitano di quanto segue:

  1. Accesso a /dev/vndbinder .
  2. Binder {transfer, call} si collega a vndservicemanager .
  3. binder_call(A, B) per qualsiasi dominio del fornitore A che desidera chiamare il dominio del fornitore B tramite l'interfaccia del raccoglitore del fornitore.
  4. Autorizzazione a {add, find} servizi in vndservicemanager .

Per soddisfare i requisiti 1 e 2, utilizzare la macro vndbinder_use() :

vndbinder_use(some_vendor_process_domain);

Per soddisfare il requisito 3, binder_call(A, B) per i processi del fornitore A e B che devono parlare tramite binder può rimanere attivo e non è necessario rinominarlo.

Per soddisfare il requisito 4, è necessario apportare modifiche al modo in cui vengono gestiti i nomi dei servizi, le etichette dei servizi e le regole.

Per i dettagli su SELinux, vedere Security-Enhanced Linux in Android . Per i dettagli su SELinux in Android 8.0, vedere SELinux per Android 8.0 .

Nomi di servizio

In precedenza, il fornitore elaborava i nomi dei servizi registrati in un file service_contexts e aggiungeva le regole corrispondenti per l'accesso a tale file. Esempio di file service_contexts 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 che migrano a vndservicemanager (e che sono già nel vecchio file service_contexts ) devono essere aggiunti al nuovo file vndservice_contexts .

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 file vndservice.te . Esempio:

type atfwd_service,      vndservice_manager_type;

Regole del gestore dei servizi

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, tali 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;