Auf dieser Seite wird beschrieben, wie Sie Dienste registrieren und finden und wie Sie Daten an einen Dienst senden, indem Sie Methoden aufrufen, die in Schnittstellen in .hal
-Dateien definiert sind.
Registrierungsdienste
HIDL-Schnittstellenserver (Objekte, die die Schnittstelle implementieren) können als benannte Dienste registriert werden. Der registrierte Name muss nicht mit dem Namen der Schnittstelle oder dem Paketnamen übereinstimmen. Wenn kein Name angegeben ist, wird der Name „default“ verwendet. Dieser sollte für HALs verwendet werden, für die nicht zwei Implementierungen derselben Schnittstelle registriert werden müssen. Beispiel für den C++-Aufruf zur Dienstregistrierung, der in jeder Schnittstelle definiert ist:
status_t status = myFoo->registerAsService(); status_t anotherStatus = anotherFoo->registerAsService("another_foo_service"); // if needed
Die Version einer HIDL-Schnittstelle ist in der Schnittstelle selbst enthalten. Sie wird automatisch mit der Dienstregistrierung verknüpft und kann über einen Methodenaufruf (android::hardware::IInterface::getInterfaceVersion()
) auf jeder HIDL-Schnittstelle abgerufen werden. Serverobjekte müssen nicht registriert werden und können über HIDL-Methodenparameter an einen anderen Prozess übergeben werden, der HIDL-Methodenaufrufe an den Server sendet.
Dienste entdecken
Anfragen per Clientcode werden für eine bestimmte Schnittstelle nach Name und Version gesendet, indem getService
in der gewünschten HAL-Klasse aufgerufen wird:
// C++ sp<V1_1::IFooService> service = V1_1::IFooService::getService(); sp<V1_1::IFooService> alternateService = V1_1::IFooService::getService("another_foo_service"); // Java V1_1.IFooService service = V1_1.IFooService.getService(true /* retry */); V1_1.IFooService alternateService = V1_1.IFooService.getService("another", true /* retry */);
Jede Version einer HIDL-Schnittstelle wird als separate Schnittstelle behandelt. So können IFooService
Version 1.1 und IFooService
Version 2.2 beide als „foo_service“ registriert werden und getService("foo_service")
erhält über jede Schnittstelle den registrierten Dienst für diese Schnittstelle. Daher muss in den meisten Fällen kein Nameparameter für die Registrierung oder Erkennung angegeben werden (d. h. „default“).
Das Anbieter-Interface-Objekt spielt auch eine Rolle bei der Transportmethode der zurückgegebenen Schnittstelle. Für eine Schnittstelle IFoo
im Paket android.hardware.foo@1.0
wird für die von IFoo::getService
zurückgegebene Schnittstelle immer die Transportmethode verwendet, die im Gerätemanifest für android.hardware.foo
deklariert ist, sofern der Eintrag vorhanden ist. Ist die Transportmethode nicht verfügbar, wird nullptr zurückgegeben.
In einigen Fällen ist es möglicherweise erforderlich, sofort fortzufahren, auch wenn der Dienst nicht verfügbar ist. Dies kann beispielsweise passieren, wenn ein Kunde die Dienstbenachrichtigungen selbst verwalten möchte, oder in einem Diagnoseprogramm (z. B. atrace
), das alle Hardwaredienste abrufen muss. In diesem Fall werden zusätzliche APIs bereitgestellt, z. B. tryGetService
in C++ oder getService("instance-name", false)
in Java. Die in Java bereitgestellte Legacy API getService
muss auch für Dienstbenachrichtigungen verwendet werden. Die Verwendung dieser API verhindert nicht die Race-Bedingung, bei der sich ein Server registriert, nachdem der Client ihn mit einer dieser APIs ohne Wiederholung angefordert hat.
Benachrichtigungen zum Ende von Diensten
Clients, die benachrichtigt werden möchten, wenn ein Dienst beendet wird, können vom Framework entsprechende Benachrichtigungen erhalten. Damit der Kunde Benachrichtigungen erhält, muss er Folgendes tun:
- Erstellen Sie eine Unterklasse der HIDL-Klasse/-Schnittstelle
hidl_death_recipient
(in C++-Code, nicht in HIDL). - Überschreiben Sie die
serviceDied()
-Methode. - Instanziieren Sie ein Objekt der untergeordneten Klasse
hidl_death_recipient
. - Rufen Sie die Methode
linkToDeath()
des zu überwachenden Dienstes auf und geben Sie dabei das Interface-Objekt vonIDeathRecipient
an. Hinweis: Mit dieser Methode wird nicht die Inhaberschaft des Erben oder des Proxys übernommen, auf den sie aufgerufen wird.
Ein Pseudocode-Beispiel (C++ und Java sind ähnlich):
class IMyDeathReceiver : hidl_death_recipient { virtual void serviceDied(uint64_t cookie, wp<IBase>& service) override { log("RIP service %d!", cookie); // Cookie should be 42 } }; .... IMyDeathReceiver deathReceiver = new IMyDeathReceiver(); m_importantService->linkToDeath(deathReceiver, 42);
Derselbe Empfänger von Todesnachrichten kann bei mehreren verschiedenen Diensten registriert sein.
Datenübertragung
Daten können an einen Dienst gesendet werden, indem Methoden aufgerufen werden, die in .hal
-Dateien in Schnittstellen definiert sind. Es gibt zwei Arten von Methoden:
- Bei blockierenden Methoden wird gewartet, bis der Server ein Ergebnis liefert.
- Bei Einwegmethoden werden Daten nur in eine Richtung gesendet und nicht blockiert. Wenn die Menge der Daten in RPC-Aufrufen die Implementierungslimits überschreitet, werden die Aufrufe entweder blockiert oder es wird eine Fehlermeldung zurückgegeben (das Verhalten ist noch nicht festgelegt).
Eine Methode, die keinen Wert zurückgibt, aber nicht als oneway
deklariert ist, ist weiterhin blockierend.
Alle in einer HIDL-Schnittstelle deklarierten Methoden werden in eine einzige Richtung aufgerufen, entweder von der HAL oder in die HAL. Die Schnittstelle gibt nicht an, in welcher Richtung sie aufgerufen wird. Architekturen, bei denen Anrufe von der HAL ausgehen müssen, sollten zwei oder mehr Schnittstellen im HAL-Paket bereitstellen und die entsprechende Schnittstelle aus jedem Prozess bereitstellen. Die Begriffe Client und Server beziehen sich auf die Aufrufrichtung der Schnittstelle. Die HAL kann also ein Server einer Schnittstelle und ein Client einer anderen Schnittstelle sein.
Callbacks
Der Begriff Callback bezieht sich auf zwei verschiedene Konzepte, die sich durch synchronen Callback und asynchronen Callback unterscheiden.
Synchrone Callbacks werden in einigen HIDL-Methoden verwendet, die Daten zurückgeben. Eine HIDL-Methode, die mehr als einen Wert zurückgibt (oder einen Wert eines nicht primitiven Typs), gibt die Ergebnisse über eine Rückruffunktion zurück. Wenn nur ein Wert zurückgegeben wird und es sich um einen primitiven Typ handelt, wird kein Callback verwendet und der Wert wird von der Methode zurückgegeben. Der Server implementiert die HIDL-Methoden und der Client die Callbacks.
Mit asynchronen Rückrufen kann der Server einer HIDL-Schnittstelle Aufrufe starten. Dazu wird eine Instanz einer zweiten Schnittstelle durch die erste Schnittstelle übergeben. Der Client der ersten Schnittstelle muss als Server der zweiten Schnittstelle fungieren. Der Server der ersten Schnittstelle kann Methoden auf dem zweiten Schnittstellenobjekt aufrufen. Beispielsweise kann eine HAL-Implementierung Informationen asynchron an den Prozess zurücksenden, der sie verwendet, indem Methoden auf einem von diesem Prozess erstellten und bereitgestellten Interface-Objekt aufgerufen werden. Methoden in Schnittstellen, die für asynchrone Callbacks verwendet werden, können blockieren (und Werte an den Aufrufer zurückgeben) oder oneway
sein. Ein Beispiel finden Sie unter „Asynchronous callbacks“ (Asynchrone Rückrufe) in HIDL C++.
Um die Speicherverwaltung zu vereinfachen, akzeptieren Methodenaufrufe und Callbacks nur in
-Parameter und unterstützen keine out
- oder inout
-Parameter.
Limits pro Transaktion
Die Menge der in HIDL-Methoden und ‑Callbacks gesendeten Daten ist nicht pro Transaktion begrenzt. Aufrufe, die 4 KB pro Transaktion überschreiten, gelten jedoch als übermäßig. In diesem Fall wird empfohlen, die entsprechende HIDL-Schnittstelle neu zu strukturieren. Eine weitere Einschränkung sind die Ressourcen, die der HIDL-Infrastruktur für die Verarbeitung mehrerer gleichzeitiger Transaktionen zur Verfügung stehen. Mehrere Transaktionen können gleichzeitig ausgeführt werden, wenn mehrere Threads oder Prozesse Aufrufe an einen Prozess oder mehrere oneway
-Aufrufe senden, die vom empfangenden Prozess nicht schnell verarbeitet werden. Der maximale Gesamtspeicherplatz, der für alle gleichzeitigen Transaktionen verfügbar ist, beträgt standardmäßig 1 MB.
Bei einer gut gestalteten Benutzeroberfläche sollte es nicht vorkommen, dass diese Ressourcenbeschränkungen überschritten werden. Andernfalls kann der Aufruf, der sie überschreitet, entweder blockiert werden, bis Ressourcen verfügbar sind, oder einen Transportfehler signalisieren. Jedes Mal, wenn das Limit pro Transaktion überschritten wird oder die HIDL-Implementierungsressourcen durch aggregierte In-Flight-Transaktionen überlaufen, wird dies protokolliert, um die Fehlerbehebung zu erleichtern.
Methodenimplementierungen
HIDL generiert Headerdateien, in denen die erforderlichen Typen, Methoden und Rückrufe in der Zielsprache (C++ oder Java) deklariert werden. Der Prototyp von HIDL-definierten Methoden und Callbacks ist sowohl für Client- als auch für Servercode identisch. Das HIDL-System bietet Proxy-Implementierungen der Methoden auf der Aufruferseite, die die Daten für den IPC-Transport organisieren, und Stub-Code auf der Aufrufnehmerseite, der die Daten an Entwicklerimplementierungen der Methoden weitergibt.
Der Aufrufer einer Funktion (HIDL-Methode oder Callback) hat die Inhaberschaft der Datenstrukturen, die an die Funktion übergeben werden, und behält sie auch nach dem Aufruf. In allen Fällen muss der Gerufene den Speicher nicht freigeben.
- In C++ sind die Daten möglicherweise nur lesbar (Schreibversuche können zu einem Segmentierungsfehler führen) und gültig für die Dauer des Aufrufs. Der Client kann eine Deep-Copy der Daten erstellen, um sie über den Aufruf hinaus zu übertragen.
- In Java erhält der Code eine lokale Kopie der Daten (ein normales Java-Objekt), die er beibehalten und ändern oder zur Garbage Collection freigeben kann.
Nicht-RPC-Datenübertragung
HIDL bietet zwei Möglichkeiten, Daten ohne RPC-Aufruf zu übertragen: freigegebenen Arbeitsspeicher und eine Fast Message Queue (FMQ). Beide werden nur in C++ unterstützt.
- Gemeinsamer Speicher Der integrierte HIDL-Typ
memory
wird verwendet, um ein Objekt zu übergeben, das den zugewiesenen gemeinsamen Speicher darstellt. Kann in einem Empfängerprozess verwendet werden, um den gemeinsamen Speicher zuzuordnen. - Schnelle Nachrichtenwarteschlange (Fast Message Queue, FMQ) HIDL bietet einen typbasierten Nachrichtenwarteschlangentyp, der die Nachrichtenweitergabe ohne Wartezeit implementiert. Der Kernel oder Scheduler wird im Passthrough- oder Binder-Modus nicht verwendet, da die Gerätekommunikation diese Eigenschaften nicht hat. Normalerweise richtet die HAL ihr Ende der Warteschlange ein und erstellt ein Objekt, das über einen Parameter des integrierten HIDL-Typs
MQDescriptorSync
oderMQDescriptorUnsync
an RPC übergeben werden kann. Dieses Objekt kann vom empfangenden Prozess verwendet werden, um das andere Ende der Warteschlange einzurichten.- Synchronisierungswarteschlangen dürfen nicht überlaufen und können nur einen Leser haben.
- Nicht synchronisierte Warteschlangen dürfen überlaufen und viele Leser haben, von denen jeder die Daten rechtzeitig lesen muss, da sie sonst verloren gehen.
Weitere Informationen zu FMQ finden Sie unter Schnelle Nachrichtenwarteschlange (Fast Message Queue, FMQ).