Użyj IPC powiązania

Na tej stronie opisujemy zmiany w sterowniku bindera w Androidzie 8, przedstawiamy szczegóły dotyczące korzystania z binder IPC oraz podajemy wymagane zasady SELinux.

Zmiany w sterowniku wiązania

Począwszy od Androida 8, platforma Android i interfejsy HAL komunikują się ze sobą za pomocą bindera. Ponieważ taka komunikacja znacznie zwiększa ruch bindera, Android 8 zawiera kilka ulepszeń, które mają na celu zapewnienie szybkiego działania interfejsu binder IPC. Dostawcy SoC i OEM-y powinni bezpośrednio łączyć odpowiednie gałęzie projektu kernel/common w wersjach android-4.4, android-4.9 lub nowszych.

Wiele domen bindera (kontekstów)

Wersja wspólna 4.4 i nowsze, w tym wstecz

Aby wyraźnie rozdzielić ruch bindera między kod frameworku (niezależny od urządzenia) a kod dostawcy (specyficzny dla urządzenia), w Androidzie 8 wprowadzono pojęcie kontekstu bindera. Każdy kontekst bindera ma własny węzeł urządzenia i własny menedżer kontekstu (usługi). Dostęp do menedżera kontekstu można uzyskać tylko przez węzeł urządzenia, do którego należy. Po przekazaniu węzła bindera w ramach określonego kontekstu jest on dostępny z tego samego kontekstu tylko przez inny proces, co pozwala na całkowite oddzielenie domen od siebie. Szczegółowe informacje o używaniu tych narzędzi znajdziesz w artykułach vndbindervndservicemanager.

Rozproszenie-zbieranie

Wersja wspólna 4.4 i nowsze, w tym wstecz

W poprzednich wersjach Androida każdy element danych w połączeniu było kopiowany trzykrotnie:

  • Po zserializowaniu go w Parcelw procesie wywoływania
  • W sterowniku jądra skopiuj Parcel do procesu docelowego.
  • raz, aby zdeserializować obiekt Parcel w docelowym procesie;

Android 8 korzysta z optymalizacji rozpraszania i gromadzenia, aby zmniejszyć liczbę kopii z 3 do 1. Zamiast serializacji danych w Parcel, dane pozostają w pierwotnej strukturze i układzie pamięci, a sterownik od razu kopiuje je do procesu docelowego. Gdy dane znajdą się w docelowym procesie, ich struktura i układ pamięci pozostaną takie same, a dane będą mogły być odczytywane bez konieczności tworzenia kopii.

Szczegółowy blokowanie

Wersja wspólna 4.4 i nowsze, w tym wstecz

W poprzednich wersjach Androida sterownik bindera używał globalnego blokowania w celu ochrony przed jednoczesnym dostępem do krytycznych struktur danych. Chociaż rywalizacja o blokadę była minimalna, głównym problemem było to, że jeśli wątek o niskim priorytecie uzyskał blokadę, a następnie został zablokowany, mogło to znacznie opóźnić wątki o wyższym priorytecie, które potrzebowały tej samej blokady. Powodowało to zakłócenia w działaniu platformy.

Początkowe próby rozwiązania tego problemu polegały na wyłączeniu zatrzymywania i utrzymaniu blokady globalnej. Było to jednak raczej obejście problemu niż prawdziwe rozwiązanie, które zostało ostatecznie odrzucone przez źródło i odrzucone. Kolejne próby skupiały się na bardziej szczegółowym blokowaniu. Wersja, która jest używana na urządzeniach Pixel, działa od stycznia 2017 roku. Chociaż większość tych zmian została opublikowana, w kolejnych wersjach wprowadzono istotne ulepszenia.

Po wykryciu drobnych problemów w implementacji szczegółowego blokowania opracowaliśmy ulepszone rozwiązanie z inną architekturą blokowania i przesłaliśmy zmiany we wszystkich typowych gałęziach jądra. Nadal testujemy tę implementację na wielu różnych urządzeniach. Nie wiemy o żadnych nierozwiązanych problemach, dlatego jest to zalecana implementacja na urządzeniach z Androidem 8.

Dziedziczenie priorytetu w czasie rzeczywistym

Common-4.4 i common-4.9 (upstream wkrótce)

Sterownik bindera zawsze obsługiwał ładne dziedziczenie priorytetów. W miarę jak rośnie liczba procesów w Androidzie, które działają z priorytetem czasu rzeczywistego, w niektórych przypadkach ma to sens, jeśli wątek w czasie rzeczywistym wywołuje binder, wątek w procesie, który obsługuje to wywołanie, również działa z priorytetem czasu rzeczywistego. Aby obsługiwać te przypadki użycia, Android 8 wdraża dziedziczenie priorytetów w czasie rzeczywistym w sterowniku bindera.

Oprócz dziedziczenia priorytetu na poziomie transakcji dziedziczenie priorytetu węzła pozwala węzłowi (obiekt usługi bindera) określić minimalny priorytet, z jakim powinny być wykonywane wywołania tego węzła. W poprzednich wersjach Androida dziedziczenie priorytetu węzła było obsługiwane za pomocą wartości ładnych, ale Android 8 umożliwia dziedziczenie węzła zasad harmonogramowania w czasie rzeczywistym.

Zmiany w przestrzeni użytkownika

Android 8 zawiera wszystkie zmiany w przestrzeni użytkownika wymagane do działania z obecnym sterownikiem bindera w ramach wspólnego jądra z jednym wyjątkiem: pierwotna implementacja wyłączania dziedziczenia priorytetu w czasie rzeczywistym w przypadku /dev/binder używała polecenia ioctl. W kolejnych wersjach kontrola dziedziczenia priorytetów została zastąpiona bardziej szczegółową metodą, która jest zależna od trybu bindera (a nie od kontekstu). Dlatego ioctl nie znajduje się w gałęzi wspólnej Androida, a zamiast tego jest przesyłany w ramach naszych wspólnych jąder.

W efekcie dziedziczenie priorytetu w czasie rzeczywistym jest domyślnie wyłączone w przypadku każdego węzła. Zespół ds. wydajności Androida stwierdził, że korzystne jest włączenie dziedziczenia priorytetów w czasie rzeczywistym we wszystkich węzłach w domenie hwbinder. Aby uzyskać ten sam efekt, wybierz tą zmianę w przestrzeni użytkownika.

SHA dla typowych jąder

Aby wprowadzić niezbędne zmiany w sterowniku bindera, zsynchronizuj się z odpowiednim identyfikatorem SHA:

  • 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.

Praca z interfejsem IPC notesektu

W przeszłości procesy dostawców korzystały z komunikacji międzyprocesowej bindera (IPC). W Androidzie 8 węzeł urządzenia /dev/binder staje się dostępny wyłącznie dla procesów frameworka, co oznacza, że procesy dostawców nie mają już do niego dostępu. Procesy dostawcy mogą uzyskiwać dostęp do /dev/hwbinder, ale muszą przekonwertować swoje interfejsy AIDL na interfejsy HIDL. Dostawcy, którzy chcą nadal używać interfejsów AIDL między procesami dostawcy, mogą korzystać z interfejsu binder IPC w sposób opisany poniżej. W Androidzie 10 interfejs Stable AIDL pozwala wszystkim procesom korzystać z interfejsu /dev/binder, a także zapewnia stabilność, gwarantując zgodność z HIDL i interfejsem /dev/hwbinder. Informacje o używaniu stabilnej wersji AIDL znajdziesz w artykule AIDL dla HAL-i.

vndbinder

Android 8 obsługuje nową domenę bindera na potrzeby usług dostawców. Dostęp do niej uzyskuje się za pomocą /dev/vndbinder zamiast /dev/binder. Dzięki dodaniu /dev/vndbinder Android ma teraz 3 domeny IPC:

Domena IPC Opis
/dev/binder IPC między procesami frameworku/aplikacji a interfejsami AIDL
/dev/hwbinder IPC między procesami frameworku lub dostawcy za pomocą interfejsów HIDL
IPC między procesami dostawcy za pomocą interfejsów HIDL
/dev/vndbinder IPC między dostawcami lub procesami dostawców za pomocą interfejsów AIDL

Aby opcja /dev/vndbinder była widoczna, sprawdź, czy element konfiguracji jądra CONFIG_ANDROID_BINDER_DEVICES ma wartość "binder,hwbinder,vndbinder" (jest to wartość domyślna w powszechnie stosowanych drzewach jądra Androida).

Zazwyczaj procesy dostawcy nie otwierają bezpośrednio sterownika bindera, lecz łączą się z biblioteką libbinder w przestrzeni użytkownika, która otwiera sterownik bindera. Dodanie metody dla ::android::ProcessState()wybiera sterownik złącza dla libbinder. Procesy dostawcy powinny wywoływać tę metodę przed wywołaniem funkcji ProcessState, IPCThreadState lub przed wywołaniem dowolnej funkcji bindera. Aby go użyć, wykonaj to wywołanie po main() procesu dostawcy (klient i serwer):

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

vndservicemanager

Wcześniej usługi binder były rejestrowane w servicemanager, gdzie mogły być pobierane przez inne procesy. W Androidzie 8 zmienna servicemanager jest używana wyłącznie przez procesy frameworku i aplikacji, a procesy dostawców nie mają już do niej dostępu.

Usługi dostawcy mogą jednak teraz używać vndservicemanager, nowej instancji servicemanager, która korzysta z /dev/vndbinder zamiast z /dev/binder i która jest tworzona z tych samych źródeł co framework servicemanager. Procesy dostawcy nie wymagają wprowadzania zmian, aby komunikować się z poziomem vndservicemanager. Gdy proces dostawcy otwiera się w poziomie /dev/vndbinder, wyszukiwanie usług jest automatycznie kierowane do poziomu vndservicemanager.

Plik binarny vndservicemanager jest dołączony do domyślnych plików make urządzenia Android.

Zasady SELinux

Procesy dostawcy, które chcą korzystać z funkcji bindera do komunikacji ze sobą, muszą spełniać te wymagania:

  1. Dostęp do usługi /dev/vndbinder.
  2. Segregator {transfer, call} jest podłączony do vndservicemanager.
  3. binder_call(A, B) dla domeny dostawcy A, która chce wywołać domenę dostawcy B za pomocą interfejsu dostawcy.
  4. Uprawnienia do usług {add, find}vndservicemanager.

Aby spełnić wymagania 1 i 2, użyj makro vndbinder_use():

vndbinder_use(some_vendor_process_domain);

Aby spełnić wymagania 3, usługa binder_call(A, B) dla procesów dostawcy A i B, które muszą się komunikować za pomocą usługi binder, może pozostać na swoim miejscu i nie wymaga zmiany nazwy.

Aby spełnić wymóg 4, musisz wprowadzić zmiany w sposobie obsługi nazw usług, etykiet usług i zasad.

Szczegółowe informacje o SELinux znajdziesz w artykule SELinux (Security-Enhanced Linux) w Androidzie. Więcej informacji o SELinux w Androidzie 8.0 znajdziesz w artykule SELinux w Androidzie 8.0.

Nazwy usług

Wcześniej dostawca przetwarzał zarejestrowane nazwy usług w pliku service_contexts i dodawał odpowiednie reguły dostępu do tego pliku. Przykładowy plik service_contexts z poziomu 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

W Androidzie 8 polecenie vndservicemanager wczytuje zamiast tego plik vndservice_contexts. Usługi dostawcy migrujące do pliku vndservicemanager (i znajdujące się już w starym pliku service_contexts) należy dodać do nowego pliku vndservice_contexts.

Etykiety usług

Wcześniej etykiety usług, takie jak u:object_r:atfwd_service:s0, były definiowane w pliku service.te. Przykład:

type atfwd_service,      service_manager_type;

W Androidzie 8 musisz zmienić typ na vndservice_manager_type i przenieść regułę do pliku vndservice.te. Przykład:

type atfwd_service,      vndservice_manager_type;

reguły servicemanagera,

Wcześniej reguły przyznawały domenom dostęp do dodawania i wyszukiwania usług z servicemanager. Przykład:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;

W Androidzie 8 takie reguły mogą pozostać na swoim miejscu i korzystać z tej samej klasy. Przykład:

allow atfwd atfwd_service:service_manager find;
allow some_vendor_app atfwd_service:service_manager add;