Utilizzando Binder IPC

Questa pagina descrive le modifiche al driver del raccoglitore in Android 8, fornisce dettagli sull'utilizzo del raccoglitore IPC 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 utilizzando il raccoglitore. Poiché questa comunicazione aumenta notevolmente il traffico dei raccoglitori, Android 8 include numerosi miglioramenti progettati per mantenere veloce l'IPC dei raccoglitori. I fornitori e gli OEM di SoC dovrebbero fondersi direttamente dai rami pertinenti di Android-4.4, Android-4.9 e versioni successive del kernel/progetto comune .

Domini di raccoglitori multipli (contesti)

Common-4.4 e versioni successive, incluso upstream

Per suddividere in modo netto il traffico del raccoglitore tra il codice del framework (indipendente dal dispositivo) e quello del fornitore (specifico del dispositivo), Android 8 ha introdotto il concetto di contesto del raccoglitore . Ogni contesto del raccoglitore ha il proprio nodo di dispositivo e il proprio gestore del contesto (servizio). È possibile accedere al gestore del contesto solo attraverso il nodo del dispositivo a cui appartiene e, quando si passa un nodo binder attraverso un determinato contesto, è accessibile da quello stesso contesto solo tramite un altro processo, isolando così completamente i domini l'uno dall'altro. Per i dettagli sull'utilizzo, vedere vndbinder e vndservicemanager .

Scatter-raccolta

Common-4.4 e versioni successive, incluso upstream

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

  • Once per serializzarlo in un Parcel nel processo di chiamata
  • Una volta nel driver del kernel, copiare il Parcel nel processo di destinazione
  • Una volta per annullare la serializzazione del 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 e nel layout di memoria originali e il driver li copia immediatamente nel processo di destinazione. Una volta che i dati si trovano 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.

Chiusura a grana fine

Common-4.4 e versioni successive, incluso upstream

Nelle versioni precedenti di Android, il driver del raccoglitore utilizzava un blocco globale per proteggere dall'accesso simultaneo alle strutture dati critiche. Anche se il conflitto per il blocco era minimo, il problema principale era che se un thread a bassa priorità otteneva il blocco e poi veniva annullato, poteva ritardare seriamente i thread con priorità più alta che dovevano ottenere lo stesso blocco. Ciò ha causato problemi alla piattaforma.

I tentativi iniziali di risolvere questo problema prevedevano la disabilitazione della prelazione mantenendo il blocco globale. Tuttavia, si trattava più di un hack che di una vera soluzione, e alla fine fu rifiutato dagli upstream e scartato. I tentativi successivi si sono concentrati sul rendere il blocco più capillare, 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 presentato le modifiche in tutti i rami comuni del kernel. 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.

Eredità della priorità in tempo reale

Common-4.4 e common-4.9 (upstream in arrivo)

Il driver del raccoglitore ha sempre supportato l'ereditarietà delle priorità. Poiché un numero crescente di processi in Android vengono eseguiti 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 raccoglitore) 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à dei nodi con valori piacevoli, ma Android 8 aggiunge il supporto per l'ereditarietà dei nodi delle politiche di pianificazione in tempo reale.

Cambiamenti nello spazio utente

Android 8 include tutte le modifiche allo spazio utente necessarie per funzionare con l'attuale driver del raccoglitore 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 spostato il controllo dell'ereditarietà della priorità in un metodo più dettagliato, ovvero 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 dedicato alle 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 questa modifica nello spazio utente.

SHA per kernel comuni

Per ottenere le modifiche necessarie al driver del raccoglitore, sincronizzare con lo SHA appropriato:

  • Comune-3.18
    cc8b90c121de ANDROID: raccoglitore: non controllare i permessi 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 momento del ripristino.

Utilizzando il legante IPC

Storicamente, i processi dei fornitori hanno utilizzato la comunicazione interprocesso legante (IPC) per comunicare. In Android 8, il nodo del dispositivo /dev/binder diventa esclusivo dei 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 del fornitore, Android supporta il raccoglitore IPC come descritto di seguito. In Android 10, AIDL stabile consente a tutti i processi di utilizzare /dev/binder risolvendo anche le garanzie di stabilità HIDL e /dev/hwbinder risolte. Per informazioni su come utilizzare AIDL stabile, vedere AIDL per HAL .

vndbinder

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

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

Affinché /dev/vndbinder appaia, assicurati che l'elemento di configurazione del kernel CONFIG_ANDROID_BINDER_DEVICES sia impostato su "binder,hwbinder,vndbinder" (questo è 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 al raccoglitore in generale. Per utilizzarlo, inserire la seguente chiamata dopo main() di un processo del fornitore (client e server):

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

vndservicemanager

In precedenza, i servizi raccoglitore venivano registrati con servicemanager , dove potevano essere recuperati da altri processi. In Android 8, servicemanager viene ora utilizzato esclusivamente dai processi del framework e 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 è creata dalle stesse fonti del framework servicemanager . I processi del fornitore non necessitano di apportare modifiche per comunicare con vndservicemanager ; quando il processo di un fornitore apre / dev/vndbinder , le ricerche dei servizi vanno automaticamente a vndservicemanager .

Il file binario vndservicemanager è incluso nei makefile predefiniti del dispositivo Android.

Politica SELinux

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

  1. Accesso a /dev/vndbinder .
  2. Il raccoglitore {transfer, call} si aggancia 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 dialogare sul binder può rimanere al suo posto 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 dettagli su SELinux, vedere Linux con sicurezza avanzata in Android . Per dettagli su SELinux in Android 8.0, vedere 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 l'accesso a quel 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 ) dovrebbero 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 direttore del servizio

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;