Servicios y transferencia de datos

En esta página, se describe cómo registrar y descubrir servicios, y cómo enviar datos a un servicio llamando a los métodos definidos en interfaces de .hal. archivos.

Registrar servicios

Se pueden registrar servidores de interfaz HIDL (objetos que implementan la interfaz) como servicios con nombre. No es necesario que el nombre registrado esté relacionado con la interfaz o el nombre del paquete. Si no se especifica ningún nombre, se usará el nombre “default” si se usa una de las siguientes fuentes: esto debería usarse para las HAL que no necesitan registrar dos implementaciones de la misma interfaz de usuario. Por ejemplo, la llamada de C++ para el registro de servicio definido en cada interfaz de usuario es:

status_t status = myFoo->registerAsService();
status_t anotherStatus = anotherFoo->registerAsService("another_foo_service");  // if needed

La versión de una interfaz HIDL se incluye en la propia interfaz. Sí se asocia automáticamente con el registro de servicio y se puede recuperar a través de un llamada de método (android::hardware::IInterface::getInterfaceVersion()) en cada interfaz HIDL. No es necesario registrar los objetos del servidor, ya que se pueden pasar a través de los parámetros del método HIDL a otro proceso que hace llamadas al método HIDL al servidor.

Descubre servicios

Las solicitudes por código de cliente se realizan para una interfaz determinada por nombre y por llamada a getService en la clase de HAL deseada:

// 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 */);

Cada versión de una interfaz HIDL se trata como una interfaz independiente. Por lo tanto, IFooService versión 1.1 y IFooService versión 2.2 se pueden registrar como “foo_service” y getService("foo_service") en cualquiera de las interfaces obtiene el archivo servicio para esa interfaz. Por eso, en la mayoría de los casos, no se necesita ningún parámetro de nombre que se suministrará para su registro o descubrimiento (es decir, el nombre “predeterminado”).

El objeto de interfaz de proveedor también cumple un papel en el método de transporte del objeto en la interfaz de usuario. Para una interfaz IFoo en el paquete android.hardware.foo@1.0, la interfaz que muestra IFoo::getService siempre usa el método de transporte declarado para android.hardware.foo en el manifiesto del dispositivo, si la entrada existe; Si el método de transporte no está disponible, se muestra el valor nullptr.

En algunos casos, puede ser necesario continuar inmediatamente sin obtener el servicio. Esto puede ocurrir, por ejemplo, cuando un cliente quiere administrar las notificaciones del servicio por su cuenta o en un programa de diagnóstico (como atrace) que necesita obtener todos los servicios de trabajo y recuperarlos. En en este caso, se proporcionan APIs adicionales, como tryGetService en C++ o getService("instance-name", false) en Java. La API heredada El getService proporcionado en Java también debe usarse con el notificaciones. Usar esta API no evita la condición de carrera en la que un servidor se registra después de que el cliente lo solicita con una de estas APIs sin reintento.

Notificaciones de cierre del servicio

Los clientes que quieran recibir una notificación cuando falle un servicio puede recibir la muerte y las notificaciones entregadas por el framework. Para recibir notificaciones, el cliente debe:

  1. Crea una subclase de la clase o interfaz de HIDL hidl_death_recipient (en C++) no en HIDL).
  2. Anula su método serviceDied().
  3. Crea una instancia de un objeto de la subclase hidl_death_recipient.
  4. Llama al método linkToDeath() en el servicio que deseas supervisar. y pasa el objeto de interfaz de IDeathRecipient. Ten en cuenta que esta no se haga cargo del destinatario de la muerte o del proxy en el que se llama.

Un ejemplo de seudocódigo (C++ y Java son similares):

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);

El mismo destinatario de la muerte puede registrarse en varios servicios diferentes.

Transferencia de datos

Los datos se pueden enviar a un servicio llamando a los métodos definidos en las interfaces de .hal. Existen dos tipos de métodos:

  • Los métodos de bloqueo esperan hasta que el servidor produzca un resultado.
  • Los métodos unidireccionales envían datos en una sola dirección, pero no bloque. Si la cantidad de datos en tránsito en las llamadas RPC supera la implementación límites, las llamadas pueden bloquear o mostrar un indicador de error (el comportamiento es aún sin determinar).

Un método que no devuelve un valor, pero no se declara como oneway continúa bloqueando el bloqueo.

Todos los métodos declarados en una interfaz HIDL se llaman en una sola dirección ya sea desde la HAL o hacia la HAL. La interfaz no especifica qué dirección a la que se llama. Arquitecturas que necesitan que las llamadas se originen desde la HAL debe proporcionar dos (o más) interfaces en el paquete de HAL y mostrar la interfaz adecuada de cada proceso. Las palabras cliente y server con respecto a la dirección de llamada de la interfaz (es decir, la HAL puede ser un servidor de una interfaz y un cliente de otra de usuario).

Devoluciones de llamada

La palabra devolución de llamada hace referencia a dos conceptos diferentes, que se distinguen por devolución de llamada síncrona y devolución de llamada asíncrona.

Las devoluciones de llamada síncronas se usan en algunos métodos HIDL que devuelven de datos no estructurados. Un método HIDL que devuelve más de un valor (o devuelve un valor de tipo no primitivo) devuelve sus resultados mediante una función de devolución de llamada. Si solo una de muestra y es un tipo primitivo, no se usa una devolución de llamada y la el resultado del método. El servidor implementa los métodos HIDL y el cliente implementa las devoluciones de llamada.

Las devoluciones de llamada asíncronas permiten que el servidor de una interfaz HIDL haga lo siguiente: originan llamadas. Para ello, se pasa una instancia de una segunda interfaz a través de la primera interfaz. El cliente de la primera interfaz debe actuar como el servidor de la segunda. El servidor de la primera interfaz puede llamar a los métodos de la segundo objeto de interfaz. Por ejemplo, una implementación de HAL puede enviar información de forma asíncrona al proceso que lo usa llamando a métodos en una de interfaz de usuario que crea y entrega ese proceso. Métodos en las interfaces utilizadas para la devolución de llamada asíncrona podría generar un bloqueo (y mostrar valores al emisor). o oneway. Para ver un ejemplo, consulta “Devoluciones de llamada asíncronas” en HIDL C++:

Para simplificar la propiedad de la memoria, las llamadas a métodos y las devoluciones de llamada solo toman Parámetros in y no admiten out ni Parámetros inout.

Límites por transacción

No se aplican límites por transacción a la cantidad de datos enviados en HIDL. métodos y devoluciones de llamada. Sin embargo, se aceptan las llamadas que superan los 4 KB por transacción como excesiva. Si se observa esto, se debe rediseñar la arquitectura de la interfaz HIDL determinada. . Otra limitación son los recursos disponibles para el HIDL para manejar múltiples transacciones simultáneas. Múltiples pueden estar en tránsito simultáneamente debido a múltiples subprocesos o procesos que envían llamadas a un proceso o a varias llamadas oneway que no se manejan rápidamente con el proceso receptor. Espacio total máximo disponible para todas las transacciones simultáneas es de 1 MB de forma predeterminada.

En una interfaz bien diseñada, superar estas limitaciones de recursos no debería suceder; si lo hace, la llamada que los superó puede bloquearse hasta que recursos están disponibles o indican un error de transporte. Cada caso de exceden los límites por transacción o desbordan recursos de implementación de HIDL las transacciones agregadas en tránsito se registran para facilitar la depuración.

Implementaciones de métodos

HIDL genera archivos de encabezado que declaran los tipos, métodos y de devoluciones de llamada en el lenguaje objetivo (C++ o Java). El prototipo definido por HIDL métodos y devoluciones de llamada es la misma para el código del cliente y del servidor. El HIDL proporciona implementaciones proxy de los métodos en la emisor que organiza los datos para el transporte de IPC y stub código del destinatario que pasa los datos a implementaciones de desarrollador de los métodos.

El llamador de una función (método de devolución de llamada HIDL o devolución de llamada) tiene la propiedad de los datos. las estructuras pasadas a la función y retiene la propiedad después de la llamada; en todos los casos en que el destinatario no necesite liberar o liberar el almacenamiento.

  • En C++, los datos pueden ser de solo lectura (los intentos de escribir en ellos pueden provocar una falla de segmentación) y son válidos mientras dure la llamada. El cliente puede hacer una copia profunda de los datos para propagarlos más allá de la llamada.
  • En Java, el código recibe una copia local de los datos (un objeto Java normal), que podría conservar y modificar o permitir su recolección de elementos no utilizados.

Transferencia de datos que no son de RPC

HIDL tiene dos formas de transferir datos sin usar una llamada RPC: compartidos y una cola de mensajes rápidos (FMQ), compatibles solo con C++.

  • Memoria compartida. El tipo de HIDL integrado memory se usa para pasar un objeto que representa la memoria compartida que se asignó. Se puede usar en un proceso de recepción para asignar la memoria compartida.
  • Cola de mensajes rápidos (FMQ): El HIDL proporciona un mensaje basado en una plantilla que implementa el pase de mensajes sin espera. No usa el kernel o programador en modo transferible o vinculante (la comunicación entre dispositivos no tienen estas propiedades). Por lo general, la HAL configura el final de la cola la creación de un objeto que se puede pasar a través de RPC a través de un parámetro de Tipo de HIDL MQDescriptorSync o MQDescriptorUnsync Esta puede usar el proceso receptor para configurar el otro extremo de la cola.
    • No se permite desbordamiento de colas de sincronización y solo pueden tener una. de lectura.
    • Las colas no sincronizadas pueden desbordarse y pueden tener muchos lectores, cada uno de los cuales debe leer datos a tiempo o perderlos.
    Ninguno de los tipos puede subdesbordar (la lectura de una cola vacía falla) y cada tipo solo puede tener un escritor.

Para obtener más información sobre FMQ, consulta Cola de mensajes rápidos (FMQ):