Verwendung von Binder IPC

Auf dieser Seite werden Änderungen am Binder-Treiber in Android 8 beschrieben, Details zur Verwendung von Binder IPC bereitgestellt und die erforderliche SELinux-Richtlinie aufgeführt.

Änderungen am Binder-Treiber

Ab Android 8 kommunizieren das Android-Framework und die HALs nun über Binder miteinander. Da diese Kommunikation den Binder-Verkehr dramatisch erhöht, enthält Android 8 mehrere Verbesserungen, die den Binder-IPC schnell halten sollen. SoC-Anbieter und OEMs sollten direkt aus den relevanten Zweigen von Android-4.4, Android-4.9 und höher des Kernel-/Common- Projekts fusionieren.

Mehrere Binderdomänen (Kontexte)

Common-4.4 und höher, einschließlich Upstream

Um den Binder-Verkehr sauber zwischen Framework-Code (geräteunabhängig) und Hersteller-Code (gerätespezifisch) aufzuteilen, führte Android 8 das Konzept eines Binder-Kontexts ein. Jeder Binder-Kontext verfügt über einen eigenen Geräteknoten und einen eigenen Kontext-(Dienst-)Manager. Sie können auf den Kontextmanager nur über den Geräteknoten zugreifen, zu dem er gehört, und wenn ein Binderknoten durch einen bestimmten Kontext geleitet wird, ist er aus demselben Kontext nur für einen anderen Prozess zugänglich, wodurch die Domänen vollständig voneinander isoliert werden. Einzelheiten zur Verwendung finden Sie unter vndbinder und vndservicemanager .

Scatter-Gather

Common-4.4 und höher, einschließlich Upstream

In früheren Android-Versionen wurde jedes Datenelement in einem Ordneraufruf dreimal kopiert:

  • Einmal, um es im aufrufenden Prozess in ein Parcel zu serialisieren
  • Sobald Sie im Kernel-Treiber sind, kopieren Sie das Parcel in den Zielprozess
  • Einmal, um das Parcel im Zielprozess zu deserialisieren

Android 8 verwendet Scatter-Gather-Optimierung , um die Anzahl der Kopien von 3 auf 1 zu reduzieren. Anstatt Daten zunächst in einem Parcel zu serialisieren, bleiben die Daten in ihrer ursprünglichen Struktur und ihrem Speicherlayout und der Treiber kopiert sie sofort in den Zielprozess. Nachdem sich die Daten im Zielprozess befinden, sind Struktur und Speicherlayout gleich und die Daten können gelesen werden, ohne dass eine weitere Kopie erforderlich ist.

Feinkörnige Verriegelung

Common-4.4 und höher, einschließlich Upstream

In früheren Android-Versionen verwendete der Binder-Treiber eine globale Sperre, um vor gleichzeitigem Zugriff auf kritische Datenstrukturen zu schützen. Zwar gab es nur minimale Konflikte um die Sperre, das Hauptproblem bestand jedoch darin, dass ein Thread mit niedriger Priorität, der die Sperre erhielt und dann vorzeitig freigegeben wurde, Threads mit höherer Priorität, die dieselbe Sperre benötigen, erheblich verzögern konnte. Dies führte zu einem Ruckeln in der Plattform.

Erste Versuche, dieses Problem zu lösen, bestanden darin, die Vorbelegung zu deaktivieren und gleichzeitig die globale Sperre aufrechtzuerhalten. Dies war jedoch eher ein Hack als eine echte Lösung und wurde schließlich von den Upstream-Anbietern abgelehnt und verworfen. Nachfolgende Versuche konzentrierten sich darauf, die Sperrung feiner zu gestalten. Eine Version davon läuft seit Januar 2017 auf Pixel-Geräten. Während die meisten dieser Änderungen veröffentlicht wurden, wurden in nachfolgenden Versionen erhebliche Verbesserungen vorgenommen.

Nachdem wir kleine Probleme in der feinkörnigen Sperrimplementierung identifiziert hatten, entwickelten wir eine verbesserte Lösung mit einer anderen Sperrarchitektur und reichten die Änderungen in allen gängigen Kernelzweigen ein. Wir testen diese Implementierung weiterhin auf einer großen Anzahl verschiedener Geräte. Da uns keine offenen Probleme bekannt sind, ist dies die empfohlene Implementierung für Geräte, die mit Android 8 ausgeliefert werden.

Prioritätsvererbung in Echtzeit

Common-4.4 und Common-4.9 (Upstream in Kürze verfügbar)

Der Binder-Treiber unterstützt seit jeher die nette Prioritätsvererbung. Da in Android immer mehr Prozesse mit Echtzeitpriorität ausgeführt werden, ist es in einigen Fällen jetzt sinnvoll, dass, wenn ein Echtzeitthread einen Binder-Aufruf durchführt, der Thread im Prozess, der diesen Aufruf verarbeitet, ebenfalls mit Echtzeitpriorität ausgeführt wird . Um diese Anwendungsfälle zu unterstützen, implementiert Android 8 jetzt eine Echtzeit-Prioritätsvererbung im Binder-Treiber.

Zusätzlich zur Prioritätsvererbung auf Transaktionsebene ermöglicht die Knotenprioritätsvererbung einem Knoten (Binder-Dienstobjekt), eine Mindestpriorität anzugeben, mit der Aufrufe an diesen Knoten ausgeführt werden sollen. Frühere Versionen von Android unterstützten bereits die Knotenprioritätsvererbung mit netten Werten, aber Android 8 bietet Unterstützung für die Knotenvererbung mit Echtzeitplanungsrichtlinien.

Änderungen im Userspace

Android 8 enthält alle Userspace-Änderungen, die für die Arbeit mit dem aktuellen Binder-Treiber im gemeinsamen Kernel erforderlich sind, mit einer Ausnahme: Die ursprüngliche Implementierung zum Deaktivieren der Echtzeit-Prioritätsvererbung für /dev/binder verwendete ein ioctl . Bei der späteren Entwicklung wurde die Steuerung der Prioritätsvererbung auf eine differenziertere Methode umgestellt, die pro Bindermodus (und nicht pro Kontext) erfolgt. Daher befindet sich das ioctl nicht im gemeinsamen Android-Zweig, sondern wird stattdessen in unseren gemeinsamen Kerneln übermittelt .

Die Auswirkung dieser Änderung besteht darin, dass die Echtzeit-Prioritätsvererbung standardmäßig für jeden Knoten deaktiviert ist. Das Android-Performance-Team hat festgestellt, dass es von Vorteil ist, die Prioritätsvererbung in Echtzeit für alle Knoten in der hwbinder Domäne zu aktivieren. Um den gleichen Effekt zu erzielen, wählen Sie diese Änderung im Benutzerbereich aus.

SHAs für gängige Kernel

Um notwendige Änderungen am Binder-Treiber zu erhalten, synchronisieren Sie mit dem entsprechenden SHA:

  • Allgemein-3.18
    cc8b90c121de ANDROID: Ordner: Prior-Berechtigungen bei der Wiederherstellung nicht überprüfen.
  • Allgemein-4.4
    76b376eac7a2 ANDROID: Ordner: Prior-Berechtigungen bei der Wiederherstellung nicht überprüfen.
  • Allgemein-4.9
    ecd972d4f9b5 ANDROID: Ordner: Prior-Berechtigungen bei der Wiederherstellung nicht überprüfen.

Verwendung von Binder IPC

In der Vergangenheit nutzten Lieferantenprozesse zur Kommunikation die Binder Interprocess Communication (IPC). In Android 8 wird der Geräteknoten /dev/binder exklusiv für Framework-Prozesse, was bedeutet, dass Anbieterprozesse keinen Zugriff mehr darauf haben. Anbieterprozesse können auf /dev/hwbinder zugreifen, müssen jedoch ihre AIDL-Schnittstellen konvertieren, um HIDL zu verwenden. Für Anbieter, die weiterhin AIDL-Schnittstellen zwischen Anbieterprozessen verwenden möchten, unterstützt Android Binder IPC wie unten beschrieben. In Android 10 ermöglicht Stable AIDL allen Prozessen die Verwendung von /dev/binder und löst gleichzeitig die Stabilitätsgarantien HIDL und /dev/hwbinder . Informationen zur Verwendung von Stable AIDL finden Sie unter AIDL für HALs .

vndbinder

Android 8 unterstützt eine neue Binder-Domäne zur Verwendung durch Anbieterdienste, auf die über /dev/vndbinder anstelle von /dev/binder zugegriffen werden kann. Durch das Hinzufügen von /dev/vndbinder verfügt Android nun über die folgenden drei IPC-Domänen:

IPC-Domäne Beschreibung
/dev/binder IPC zwischen Framework-/App-Prozessen mit AIDL-Schnittstellen
/dev/hwbinder IPC zwischen Framework-/Anbieterprozessen mit HIDL-Schnittstellen
IPC zwischen Lieferantenprozessen mit HIDL-Schnittstellen
/dev/vndbinder IPC zwischen Vendor/Vendor-Prozessen mit AIDL-Schnittstellen

Damit /dev/vndbinder angezeigt wird, stellen Sie sicher, dass das Kernel-Konfigurationselement CONFIG_ANDROID_BINDER_DEVICES auf "binder,hwbinder,vndbinder" eingestellt ist (dies ist die Standardeinstellung in den gängigen Kernelbäumen von Android).

Normalerweise öffnen Anbieterprozesse den Binder-Treiber nicht direkt und verknüpfen stattdessen mit der libbinder Userspace-Bibliothek, die den Binder-Treiber öffnet. Durch das Hinzufügen einer Methode für ::android::ProcessState() wird der Binder-Treiber für libbinder ausgewählt. Anbieterprozesse sollten diese Methode aufrufen , bevor sie ProcessState, IPCThreadState aufrufen oder allgemein, bevor sie Binderaufrufe durchführen. Platzieren Sie zur Verwendung den folgenden Aufruf nach main() eines Anbieterprozesses (Client und Server):

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

vndservicemanager

Zuvor wurden Binder-Dienste bei servicemanager registriert, wo sie von anderen Prozessen abgerufen werden konnten. In Android 8 wird servicemanager nun ausschließlich von Framework- und App-Prozessen verwendet und Anbieterprozesse können nicht mehr darauf zugreifen.

Anbieterdienste können jetzt jedoch vndservicemanager verwenden, eine neue Instanz von servicemanager , die /dev/vndbinder anstelle von /dev/binder verwendet und aus denselben Quellen wie das Framework servicemanager erstellt wird. Anbieterprozesse müssen keine Änderungen vornehmen, um mit vndservicemanager zu kommunizieren. Wenn ein Anbieterprozess / dev/vndbinder öffnet, werden Dienstsuchen automatisch an vndservicemanager weitergeleitet.

Die vndservicemanager Binärdatei ist in den Standard-Geräte-Makefiles von Android enthalten.

SELinux-Richtlinie

Lieferantenprozesse, die die Binder-Funktionalität für die Kommunikation untereinander nutzen möchten, benötigen Folgendes:

  1. Zugriff auf /dev/vndbinder .
  2. Binder {transfer, call} hakt sich in vndservicemanager ein.
  3. binder_call(A, B) für jede Anbieterdomäne A, die über die Anbieter-Binder-Schnittstelle einen Aufruf in die Anbieterdomäne B durchführen möchte.
  4. Berechtigung zum {add, find} von Diensten im vndservicemanager .

Um die Anforderungen 1 und 2 zu erfüllen, verwenden Sie das Makro vndbinder_use() :

vndbinder_use(some_vendor_process_domain);

Um Anforderung 3 zu erfüllen, kann der binder_call(A, B) für die Lieferantenprozesse A und B, die über Binder kommunizieren müssen, an Ort und Stelle bleiben und muss nicht umbenannt werden.

Um Anforderung 4 zu erfüllen, müssen Sie Änderungen an der Art und Weise vornehmen, wie Dienstnamen, Dienstbezeichnungen und Regeln gehandhabt werden.

Einzelheiten zu SELinux finden Sie unter Security-Enhanced Linux in Android . Einzelheiten zu SELinux in Android 8.0 finden Sie unter SELinux für Android 8.0 .

Dienstnamen

Zuvor verarbeitete der Anbieter registrierte Dienstnamen in einer service_contexts Datei und fügte entsprechende Regeln für den Zugriff auf diese Datei hinzu. Beispiel einer service_contexts Datei aus 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 lädt vndservicemanager stattdessen die Datei vndservice_contexts . Anbieterdienste, die zu vndservicemanager migriert werden (und die sich bereits in der alten service_contexts Datei befinden), sollten der neuen vndservice_contexts Datei hinzugefügt werden.

Serviceetiketten

Zuvor wurden Dienstbezeichnungen wie u:object_r:atfwd_service:s0 in einer service.te Datei definiert. Beispiel:

type atfwd_service,      service_manager_type;

In Android 8 müssen Sie den Typ in vndservice_manager_type ändern und die Regel in die Datei vndservice.te verschieben. Beispiel:

type atfwd_service,      vndservice_manager_type;

Servicemanager-Regeln

Zuvor gewährten Regeln Domänen Zugriff zum Hinzufügen oder Suchen von Diensten im servicemanager . Beispiel:

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

In Android 8 können solche Regeln bestehen bleiben und dieselbe Klasse verwenden. Beispiel:

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