Dynamisch verfügbare HALs

Android 9 unterstützt das dynamische Herunterfahren von Android-Hardware-Subsystemen, wenn sie nicht verwendet oder nicht benötigt werden. Wenn ein Nutzer beispielsweise kein WLAN verwendet, sollten die WLAN-Subsysteme keinen Speicher, keine Energie und keine anderen Systemressourcen beanspruchen. In früheren Android-Versionen wurden HALs/Treiber auf Android-Geräten während des gesamten Bootvorgangs eines Android-Smartphones geöffnet.

Für die Implementierung des dynamischen Herunterfahrens müssen Datenflüsse verbunden und dynamische Prozesse ausgeführt werden, wie in den folgenden Abschnitten beschrieben.

Änderungen an HAL-Definitionen

Für das dynamische Herunterfahren sind Informationen erforderlich, welche Prozesse welche HAL-Schnittstellen bereitstellen (diese Informationen können auch später in anderen Kontexten nützlich sein). Außerdem dürfen Prozesse beim Starten nicht gestartet und nach dem Beenden nicht neu gestartet werden, bis sie wieder angefordert werden.

# some init.rc script associated with the HAL
service vendor.some-service-name /vendor/bin/hw/some-binary-service
    # init language extension, provides information of what service is served
    # if multiple interfaces are served, they can be specified one on each line
    interface android.hardware.light@2.0::ILight default
    # restarted if hwservicemanager dies
    # would also cause the hal to start early during boot if disabled wasn't set
    class hal
    # will not be restarted if it exits until it is requested to be restarted
    oneshot
    # will only be started when requested
    disabled
    # ... other properties

Änderungen an init und hwservicemanager

Für das dynamische Herunterfahren muss der hwservicemanager außerdem dem init mitteilen, die angeforderten Dienste zu starten. In Android 9 enthält init drei zusätzliche Steuermeldungen (z.B. ctl.start): ctl.interface_start, ctl.interface_stop und ctl.interface_restart. Mit diesen Nachrichten kann init angewiesen werden, bestimmte Hardwareschnittstellen zu aktivieren und zu deaktivieren. Wenn ein Dienst angefordert wird und nicht registriert ist, fordert hwservicemanager den Dienst zum Starten auf. Bei dynamischen HALs ist dies jedoch nicht erforderlich.

HAL-Exit ermitteln

Unter Android 9 muss der HAL-Ausstieg manuell festgelegt werden. Unter Android 10 und höher kann sie auch mit automatischen Lebenszyklen bestimmt werden.

Für das dynamische Herunterfahren sind mehrere Richtlinien erforderlich, um zu entscheiden, wann ein HAL gestartet und wann ein HAL heruntergefahren werden soll. Wenn ein HAL aus irgendeinem Grund beendet wird, wird er automatisch neu gestartet, wenn er wieder benötigt wird. Dabei werden die Informationen in der HAL-Definition und die Infrastruktur verwendet, die durch Änderungen an init und hwservicemanager bereitgestellt wird. Dazu können verschiedene Strategien gehören, z. B.:

  • Eine HAL kann „exit“ auf sich selbst anwenden, wenn jemand eine ähnliche API wie „close“ darauf aufruft. Dieses Verhalten muss in der entsprechenden HAL-Schnittstelle angegeben werden.
  • HALs können heruntergefahren werden, wenn ihre Aufgabe abgeschlossen ist (in der HAL-Datei dokumentiert).

Automatische Lebenszyklen

Android 10 bietet mehr Unterstützung für den Kernel und hwservicemanager, sodass HALs automatisch heruntergefahren werden, wenn sie keine Clients haben. Wenn Sie diese Funktion verwenden möchten, führen Sie alle Schritte unter Änderungen an HAL-Definitionen aus und gehen Sie außerdem so vor:

  • Registrieren Sie den Dienst in C++ mit LazyServiceRegistrar anstelle der Mitgliedsfunktion registerAsService, z. B.:
    // only one instance of LazyServiceRegistrar per process
    LazyServiceRegistrar registrar;
    registrar.registerAsService(myHidlService /* , "default" */);
  • Prüfen Sie, ob der HAL-Client nur dann einen Verweis auf die HAL der obersten Ebene (die bei hwservicemanager registrierte Schnittstelle) beibehält, wenn sie verwendet wird. Um Verzögerungen zu vermeiden, wenn diese Referenz in einem hwbinder-Thread gelöscht wird, der weiter ausgeführt wird, sollte der Client nach dem Löschen der Referenz auch IPCThreadState::self()->flushCommands() aufrufen, damit der Binder-Treiber über die entsprechenden Änderungen der Referenzanzahl informiert wird.