Binder-IPC verwenden

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

Änderungen am Binder-Treiber

Ab Android 8 kommunizieren das Android-Framework und die HALs jetzt über den Binder miteinander. Da diese Kommunikation den Binder-Traffic drastisch erhöht, enthält Android 8 mehrere Verbesserungen, die die Geschwindigkeit des Binder-IPC aufrechterhalten sollen. SoC-Anbieter und OEMs sollten direkt aus den entsprechenden Zweigen von android-4.4, android-4.9 und höher des Projekts kernel/common zusammenführen.

Mehrere Binder-Domains (Kontexte)

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

Um den Binder-Traffic sauber zwischen Framework-Code (geräteunabhängig) und Anbietercode (gerätespezifisch) aufzuteilen, wurde in Android 8 das Konzept eines Binder-Kontexts eingeführt. Jeder Binder-Kontext hat einen eigenen Geräteknoten und einen eigenen Kontext- bzw. Dienstmanager. Sie können nur über den Geräteknoten, zu dem er gehört, auf den Context Manager zugreifen. Wenn ein Binderknoten durch einen bestimmten Kontext übergeben wird, ist er nur über diesen Kontext für einen anderen Prozess zugänglich. So werden die Domains vollständig voneinander isoliert. Weitere Informationen zur Verwendung finden Sie unter vndbinder und vndservicemanager.

Scatter-Gather

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

In früheren Android-Releases wurden alle Daten in einem Binderaufruf dreimal kopiert:

  • Einmal, um es im Aufrufprozess in eine Parcel zu serialisieren
  • Im Kerneltreiber die Parcel in den Zielprozess kopieren
  • Einmal, um die Parcel im Zielprozess zu deserialisieren

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

Detaillierte Sperrung

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

In früheren Android-Releases wurde vom Binder-Treiber eine globale Sperre verwendet, um vor gleichzeitigem Zugriff auf kritische Datenstrukturen zu schützen. Es gab zwar nur wenig Konkurrenz um die Sperre, das Hauptproblem war jedoch, dass ein Thread mit niedriger Priorität, der die Sperre erhalten und dann vorzeitig beendet wurde, Threads mit höherer Priorität, die dieselbe Sperre benötigen, erheblich verzögern konnte. Das führte zu Rucklern auf der Plattform.

Erste Versuche, dieses Problem zu beheben, beinhalteten die Deaktivierung der Voraktivierung bei gleichzeitigem Halten der globalen Sperre. Dies war jedoch eher ein Hack als eine echte Lösung und wurde schließlich von der Vorverarbeitung abgelehnt und verworfen. Bei den nachfolgenden Versuchen lag der Schwerpunkt darauf, die Sperrung detaillierter zu gestalten. Eine Version dieser Funktion wird seit Januar 2017 auf Pixel-Geräten ausgeführt. Die meisten dieser Änderungen wurden veröffentlicht, in den nachfolgenden Versionen wurden jedoch erhebliche Verbesserungen vorgenommen.

Nachdem wir kleine Probleme bei der nutzungsorientierten Sperrimplementierung festgestellt hatten, haben wir eine verbesserte Lösung mit einer anderen Sperrarchitektur entwickelt und die Änderungen in allen gängigen Kernel-Branches eingereicht. 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 mit Android 8.

Echtzeitprioritätsübernahme

Common-4.4 und Common-4.9 (bald verfügbar)

Der Binder-Treiber hat schon immer eine schöne Prioritätsübernahme unterstützt. Da immer mehr Prozesse in Android mit Echtzeitpriorität ausgeführt werden, ist es in einigen Fällen sinnvoll, dass ein Thread im Prozess, der diesen Aufruf verarbeitet, auch mit Echtzeitpriorität ausgeführt wird, wenn ein Echtzeit-Thread einen Binderaufruf ausführt. Um diese Anwendungsfälle zu unterstützen, wird in Android 8 jetzt die Echtzeitprioritätsübernahme im Binder-Treiber implementiert.

Zusätzlich zur Prioritätsübernahme auf Transaktionsebene ermöglicht die Knotenprioritätsübernahme einem Knoten (Binder-Dienstobjekt) die Angabe einer Mindestpriorität, mit der Aufrufe an diesen Knoten ausgeführt werden sollen. Bisherige Android-Versionen unterstützten bereits die Übernahme der Knotenpriorität mit Nice-Werten. Android 8 unterstützt zusätzlich die Übernahme von Knoten durch Echtzeit-Planungsrichtlinien.

Änderungen im Userspace

Android 8 enthält alle Änderungen im Userspace, 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 Echtzeitprioritätsübernahme für /dev/binder verwendete einen ioctl. Bei der nachfolgenden Entwicklung wurde die Steuerung der Prioritätsübernahme auf eine detailliertere Methode umgestellt, die pro Binder-Modus (und nicht pro Kontext) erfolgt. Daher befindet sich das ioctl nicht im Android Common-Branch, sondern wird stattdessen in unseren gemeinsamen Kerneln eingereicht.

Durch diese Änderung ist die Echtzeitprioritätsübernahme standardmäßig für alle Knoten deaktiviert. Das Android-Leistungsteam hat festgestellt, dass es vorteilhaft ist, die Echtzeitprioritätsübernahme für alle Knoten in der hwbinder-Domain zu aktivieren. Um denselben Effekt zu erzielen, können Sie diese Änderung im Userspace auswählen.

SHAs für gängige Kernel

Wenn Sie die erforderlichen Änderungen am Binder-Treiber erhalten möchten, synchronisieren Sie ihn mit der entsprechenden 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.

Mit Binder-IPC arbeiten

Bisher haben Anbieterprozesse die Binder-IPC (Inter-Process Communication) für die Kommunikation verwendet. Unter Android 8 ist der Geräteknoten /dev/binder nur noch für Framework-Prozesse verfügbar. Anbieterprozesse haben keinen Zugriff mehr darauf. Anbieterprozesse können auf /dev/hwbinder zugreifen, müssen ihre AIDL-Schnittstellen jedoch in HIDL konvertieren. Anbieter, die weiterhin AIDL-Schnittstellen zwischen Anbieterprozessen verwenden möchten, können die unten beschriebene Binder-IPC-Funktion von Android nutzen. In Android 10 ermöglicht Stable AIDL allen Prozessen die Verwendung von /dev/binder und erfüllt gleichzeitig die Stabilitätsgarantien, die HIDL und /dev/hwbinder bieten. Informationen zur Verwendung von Stable-AIDL finden Sie unter AIDL für HALs.

vndbinder

Android 8 unterstützt eine neue Binder-Domain für die Verwendung durch Anbieterdienste, auf die über /dev/vndbinder statt /dev/binder zugegriffen wird. Mit der Hinzufügung von /dev/vndbinder gibt es in Android jetzt die folgenden drei IPC-Domains:

IPC-Domain Beschreibung
/dev/binder IPC zwischen Framework-/App-Prozessen mit AIDL-Schnittstellen
/dev/hwbinder IPC zwischen Framework-/Anbieterprozessen mit HIDL-Schnittstellen
IPC zwischen Anbieterprozessen mit HIDL-Schnittstellen
/dev/vndbinder IPC zwischen Anbieter-/Anbieterprozessen mit AIDL-Schnittstellen

Damit /dev/vndbinder angezeigt wird, muss der Kernelkonfigurationselement CONFIG_ANDROID_BINDER_DEVICES auf "binder,hwbinder,vndbinder" festgelegt sein. Dies ist die Standardeinstellung in den gemeinsamen Kernelbäumen von Android.

Normalerweise öffnen Anbieterprozesse den Bindertreiber nicht direkt, sondern verknüpfen ihn stattdessen mit der libbinder-Userspace-Bibliothek, die den Bindertreiber öffnet. Wenn Sie eine Methode für ::android::ProcessState() hinzufügen, wird der Binder-Treiber für libbinder ausgewählt. Anbieterprozesse sollten diese Methode vor dem Aufruf von ProcessState, IPCThreadState oder vor dem Ausführen von Binderaufrufen allgemein aufrufen. Führe dazu den folgenden Aufruf nach dem main() eines Anbieterprozesses (Client und Server) aus:

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

vndservicemanager

Bisher wurden Binderdienste bei servicemanager registriert, wo sie von anderen Prozessen abgerufen werden konnten. Unter Android 8 wird servicemanager jetzt ausschließlich von Framework- und App-Prozessen verwendet. 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. Bei Anbieterprozessen müssen keine Änderungen vorgenommen werden, um mit vndservicemanager zu kommunizieren. Wenn ein Anbieterprozess /dev/vndbinder öffnet, werden Dienstabfragen automatisch an vndservicemanager weitergeleitet.

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

SELinux-Richtlinie

Anbieterprozesse, die die Binder-Funktionen zur Kommunikation miteinander verwenden möchten, benötigen Folgendes:

  1. Zugriff auf /dev/vndbinder
  2. Binder {transfer, call} wird an vndservicemanager angehängt.
  3. binder_call(A, B) für jede Anbieterdomain A, die über die Anbieterbinder-Benutzeroberfläche auf die Anbieterdomain B zugreifen möchte.
  4. Berechtigung für {add, find}-Dienste in vndservicemanager

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

vndbinder_use(some_vendor_process_domain);

Um Anforderung 3 zu erfüllen, kann die binder_call(A, B) für die Anbieterprozesse A und B, die über den Binder kommunizieren müssen, unverändert bleiben und muss nicht umbenannt werden.

Um Anforderung 4 zu erfüllen, müssen Sie Änderungen an der Verarbeitung von Dienstnamen, Dienstlabels und Regeln vornehmen.

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

Dienstnamen

Bisher verarbeitete der Anbieter registrierte Dienstnamen in einer service_contexts-Datei und fügte entsprechende Zugriffsregeln für diese Datei hinzu. Beispiel für eine 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

Unter Android 8 wird unter vndservicemanager stattdessen die Datei vndservice_contexts geladen. Anbieterdienste, die zu vndservicemanager migrieren und sich bereits in der alten service_contexts-Datei befinden, sollten der neuen vndservice_contexts-Datei hinzugefügt werden.

Dienstlabels

Bisher wurden Dienstlabels wie u:object_r:atfwd_service:s0 in einer service.te-Datei definiert. Beispiel:

type atfwd_service,      service_manager_type;

Unter 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 rules

Bisher gewährten Regeln Domains Zugriff, um Dienste über servicemanager hinzuzufügen oder zu finden. Beispiel:

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

Unter Android 8 können solche Regeln beibehalten und dieselbe Klasse verwendet werden. Beispiel:

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