Android 8 vuelve a definir la arquitectura del SO Android para definir interfaces claras entre la plataforma de Android independiente del dispositivo y el código específico del dispositivo y del proveedor.
Android ya define muchas de esas interfaces en forma de interfaces de HAL, que se definen como encabezados C en hardware/libhardware
. HIDL reemplaza estas interfaces de HAL por interfaces estables con control de versión, que pueden ser interfaces HIDL del cliente y del servidor en C++ (que se describen a continuación) o Java.
En las páginas de esta sección, se describen las implementaciones de C++ de las interfaces HIDL, incluidos los detalles sobre los archivos que el compilador hidl-gen
genera automáticamente a partir de los archivos .hal
de HIDL, cómo se empaquetan estos archivos y cómo integrarlos con el código C++ que los usa.
Implementaciones de clientes y servidores
Las interfaces HIDL tienen implementaciones de cliente y servidor:
- Un cliente de una interfaz HIDL es el código que usa la interfaz llamando a métodos en ella.
- Un servidor es una implementación de una interfaz HIDL que recibe llamadas de clientes y muestra resultados (si es necesario).
En la transición de los HAL de libhardware
a los HAL de HIDL, la implementación de HAL se convierte en el servidor y el proceso que llama al HAL se convierte en el cliente. Las implementaciones predeterminadas pueden entregar HALs de transferencia y vinculación, y pueden cambiar con el tiempo:
Figura 1: Progreso del desarrollo para los HAL heredados
Crea el cliente de HAL
Comienza por incluir las bibliotecas de HAL en el archivo makefile:
- Make:
LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
- Soong:
shared_libs: [ …, android.hardware.nfc@1.0 ]
A continuación, incluye los archivos de encabezado de HAL:
#include <android/hardware/nfc/1.0/IFoo.h> … // in code: sp<IFoo> client = IFoo::getService(); client->doThing();
Crea el servidor HAL
Para crear la implementación de HAL, debes tener los archivos .hal
que representan tu HAL y ya haber generado archivos de configuración de make para tu HAL con -Lmakefile
o -Landroidbp
en hidl-gen
(./hardware/interfaces/update-makefiles.sh
hace esto para los archivos HAL internos y es una buena referencia). Cuando transfieres HAL desde libhardware
, puedes hacer mucho de este trabajo fácilmente con c2hal.
Para crear los archivos necesarios para implementar tu HAL, haz lo siguiente:
PACKAGE=android.hardware.nfc@1.0 LOC=hardware/interfaces/nfc/1.0/default/ m -j hidl-gen hidl-gen -o $LOC -Lc++-impl -randroid.hardware:hardware/interfaces \ -randroid.hidl:system/libhidl/transport $PACKAGE hidl-gen -o $LOC -Landroidbp-impl -randroid.hardware:hardware/interfaces \ -randroid.hidl:system/libhidl/transport $PACKAGE
Para que el sistema HAL funcione en modo de transferencia, debes tener la función HIDL_FETCH_IModuleName
que reside en /(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so
, donde OPTIONAL_IDENTIFIER es una cadena que identifica la implementación de transferencia. Los comandos anteriores cumplen automáticamente con los requisitos del modo de transferencia, que también crean el objetivo android.hardware.nfc@1.0-impl
, pero se puede usar cualquier extensión. Por ejemplo, android.hardware.nfc@1.0-impl-foo
usa -foo
para diferenciarse.
Si un HAL es una versión menor o una extensión de otro
HAL, se debe usar el HAL base para nombrar este objeto binario. Por ejemplo, las implementaciones de android.hardware.graphics.mapper@2.1
aún deben estar en un binario llamado android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER)
.
Por lo general, el OPTIONAL_IDENTIFIER aquí incluiría la versión real de HAL. Si asignas el nombre al objeto binario de esta manera, los clientes de 2.0 pueden recuperarlo directamente, y los clientes de 2.1 pueden transmitir la implementación.
A continuación, completa los stubs con funcionalidad y configura un daemon. Ejemplo de código de daemon (compatible con transferencia directa):
#include <hidl/LegacySupport.h> int main(int /* argc */, char* /* argv */ []) { return defaultPassthroughServiceImplementation<INfc>("nfc"); }
defaultPassthroughServiceImplementation
llama a dlopen()
para la biblioteca -impl
proporcionada y la proporciona como un servicio vinculado. Código de daemon de ejemplo (para un servicio vinculado):
int main(int /* argc */, char* /* argv */ []) { // This function must be called before you join to ensure the proper // number of threads are created. The threadpool never exceeds // size one because of this call. ::android::hardware::configureRpcThreadpool(1 /*threads*/, true /*willJoin*/); sp<INfc> nfc = new Nfc(); const status_t status = nfc->registerAsService(); if (status != ::android::OK) { return 1; // or handle error } // Adds this thread to the threadpool, resulting in one total // thread in the threadpool. We could also do other things, but // would have to specify 'false' to willJoin in configureRpcThreadpool. ::android::hardware::joinRpcThreadpool(); return 1; // joinRpcThreadpool should never return }
Por lo general, este daemon se encuentra en $PACKAGE + "-service-suffix"
(por ejemplo, android.hardware.nfc@1.0-service
), pero podría estar en cualquier lugar.
El sepolicy para una clase específica de HAL es el atributo hal_<module>
(por ejemplo, hal_nfc)
). Este atributo se debe aplicar al daemon que ejecuta un HAL particular (si el mismo proceso entrega varios HAL, se pueden aplicar varios atributos).