Esta página descreve como registrar e descobrir serviços e como enviar
dados para um serviço chamando métodos definidos em interfaces em arquivos
.hal
.
Registrar serviços
Servidores de interface HIDL (objetos que implementam a interface) podem ser registrados como serviços nomeados. O nome registrado não precisa estar relacionado ao nome da interface ou do pacote. Se nenhum nome for especificado, o nome "default" será usado. Ele deve ser usado para HALs que não precisam registrar duas implementações da mesma interface. Por exemplo, a chamada C++ para registro de serviço definida em cada interface é:
status_t status = myFoo->registerAsService(); status_t anotherStatus = anotherFoo->registerAsService("another_foo_service"); // if needed
A versão de uma interface HIDL está incluída na própria interface. Ele é
associado automaticamente ao registro de serviço e pode ser recuperado por uma
chamada de método (android::hardware::IInterface::getInterfaceVersion()
)
em todas as interfaces HIDL. Os objetos do servidor não precisam ser registrados e podem ser transmitidos
por parâmetros de método HIDL para outro processo que faz chamadas de método HIDL
no servidor.
Descobrir serviços
As solicitações pelo código do cliente são feitas para uma determinada interface por nome e
versão, chamando getService
na classe HAL desejada:
// 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 versão de uma interface HIDL é tratada como uma interface separada. Assim,
IFooService
versão 1.1 e IFooService
versão 2.2
podem ser registradas como "foo_service" e
getService("foo_service")
em qualquer interface recebe o serviço
registrado para essa interface. Por isso, na maioria dos casos, nenhum parâmetro de nome precisa ser fornecido para registro ou descoberta (ou seja, nome "padrão").
O objeto da interface do fornecedor também desempenha um papel no método de transporte da
interface retornada. Para uma interface IFoo
no pacote
android.hardware.foo@1.0
, a interface retornada por
IFoo::getService
sempre usa o método de transporte declarado para
android.hardware.foo
no manifesto do dispositivo, se a entrada existir.
Se o método de transporte não estiver disponível, o valor null será retornado.
Em alguns casos, pode ser necessário continuar imediatamente, mesmo sem
receber o serviço. Isso pode acontecer, por exemplo, quando um cliente quer
gerenciar as notificações de serviço ou em um programa de diagnóstico (como
atrace
), que precisa receber e recuperar todos os hwservices. Nesse
caso, outras APIs são fornecidas, como tryGetService
em C++ ou
getService("instance-name", false)
em Java. A API legada
getService
fornecida em Java também precisa ser usada com notificações
de serviço. O uso dessa API não evita a condição de corrida em que um servidor
se registra depois que o cliente o solicita com uma dessas APIs sem nova tentativa.
Notificações de fim de serviço
Os clientes que querem receber uma notificação quando um serviço morre podem receber notificações de morte enviadas pelo framework. Para receber notificações, o cliente precisa:
- Crie uma subclasse da classe/interface
hidl_death_recipient
da HIDL (no código C++, não na HIDL). - Substitua o método
serviceDied()
. - Instancie um objeto da subclasse
hidl_death_recipient
. - Chame o método
linkToDeath()
no serviço a ser monitorado, transmitindo o objeto de interface daIDeathRecipient
. Esse método não assume a propriedade do destinatário de falecimento ou do proxy em que ele é chamado.
Um exemplo de pseudocódigo (C++ e Java são semelhantes):
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);
O mesmo beneficiário pode ser registrado em vários serviços diferentes.
Transferência de dados
Os dados podem ser enviados para um serviço chamando métodos definidos em interfaces em
arquivos .hal
. Há dois tipos de métodos:
- Os métodos de bloqueio esperam até que o servidor produza um resultado.
- Os métodos Oneway enviam dados em apenas uma direção e não bloqueiam. Se a quantidade de dados em trânsito nas chamadas RPC exceder os limites de implementação, elas poderão bloquear ou retornar uma indicação de erro (o comportamento ainda não foi determinado).
Um método que não retorna um valor, mas não é declarado como
oneway
, ainda está bloqueando.
Todos os métodos declarados em uma interface HIDL são chamados em uma única direção, do HAL ou para o HAL. A interface não especifica em qual direção ela é chamada. As arquiteturas que precisam de chamadas originadas pelo HAL precisam fornecer duas (ou mais) interfaces no pacote HAL e oferecer a interface adequada de cada processo. As palavras cliente e servidor são usadas em relação à direção de chamada da interface (ou seja, o HAL pode ser um servidor de uma interface e um cliente de outra interface).
Callbacks
A palavra callback se refere a dois conceitos diferentes, que são distinguidos por callback síncrono e callback assíncrono.
Callbacks síncronos são usados em alguns métodos HIDL que retornam dados. Um método HIDL que retorna mais de um valor (ou retorna um valor de tipo não primitivo) retorna os resultados por uma função de callback. Se apenas um valor for retornado e for um tipo primitivo, um callback não será usado e o valor será retornado pelo método. O servidor implementa os métodos HIDL e o cliente implementa os callbacks.
Os callbacks assíncronos permitem que o servidor de uma interface HIDL origine
chamadas. Isso é feito transmitindo uma instância de uma segunda interface
pela primeira. O cliente da primeira interface precisa atuar como o
servidor da segunda. O servidor da primeira interface pode chamar métodos no
objeto da segunda interface. Por exemplo, uma implementação de HAL pode enviar informações
de forma assíncrona de volta para o processo que a está usando, chamando métodos em um
objeto de interface criado e atendido por esse processo. Os métodos em interfaces usados
para callback assíncrono podem estar bloqueando (e retornando valores para o autor da chamada)
ou oneway
. Para conferir um exemplo, consulte "Callbacks assíncronos" em
HIDL C++.
Para simplificar a propriedade da memória, as chamadas de método e os callbacks recebem apenas
parâmetros in
e não oferecem suporte a parâmetros out
ou
inout
.
Limites por transação
Os limites por transação não são impostos à quantidade de dados enviados em métodos
e callbacks do HIDL. No entanto, as chamadas que excedem 4 KB por transação são
consideradas excessivas. Se isso for observado, é recomendável reestruturar a interface HIDL
indicada. Outra limitação são os recursos disponíveis para a infraestrutura
HIDL para processar várias transações simultâneas. Várias
transações podem estar em andamento simultaneamente devido a várias linhas de execução ou
processos que enviam chamadas para um processo ou várias chamadas oneway
que
não são processadas rapidamente pelo processo de recebimento. O espaço total máximo
disponível para todas as transações simultâneas é de 1 MB por padrão.
Em uma interface bem projetada, o limite de recursos não deve ser excedido. Se isso acontecer, a chamada que excedeu o limite pode bloquear até que os recursos fiquem disponíveis ou sinalizar um erro de transporte. Cada ocorrência de excesso de limites por transação ou recursos de implementação de HIDL com transações em andamento agregadas é registrada para facilitar a depuração.
Implementações de método
O HIDL gera arquivos de cabeçalho que declaram os tipos, métodos e callbacks necessários no idioma de destino (C++ ou Java). O protótipo de métodos e callbacks definidos por HIDL é o mesmo para o código do cliente e do servidor. O sistema HIDL oferece implementações de proxy dos métodos no lado do autor da chamada que organizam os dados para o transporte de IPC e o código stub no lado do autor da chamada que transmite os dados para implementações de métodos do desenvolvedor.
O autor da chamada de uma função (método HIDL ou callback) tem a propriedade das estruturas de dados transmitidas para a função e retém a propriedade após a chamada. Em todos os casos, o autor da chamada não precisa liberar o armazenamento.
- Em C++, os dados podem ser somente leitura (as tentativas de gravação podem causar uma falha de segmentação) e são válidos durante a duração da chamada. O cliente pode fazer uma cópia profunda dos dados para propagá-los além da chamada.
- No Java, o código recebe uma cópia local dos dados (um objeto Java normal), que pode ser mantida e modificada ou pode ser coletada.
Transferência de dados não RPC
O HIDL tem duas maneiras de transferir dados sem usar uma chamada de RPC: memória compartilhada e uma fila de mensagens rápida (FMQ, na sigla em inglês), ambas com suporte apenas em C++.
- Memória compartilhada. O tipo HIDL integrado
memory
é usado para transmitir um objeto que representa a memória compartilhada alocada. Pode ser usado em um processo de recebimento para mapear a memória compartilhada. - Fila de mensagens rápidas (FMQ). O HIDL fornece um tipo de fila
de mensagens com modelo que implementa a transmissão de mensagens sem espera. Ele não usa o kernel
ou o programador no modo de passagem ou de vinculação (a comunicação entre dispositivos não
tem essas propriedades). Normalmente, o HAL configura o fim da fila,
criando um objeto que pode ser transmitido pelo RPC por um parâmetro de tipo
HIDL integrado
MQDescriptorSync
ouMQDescriptorUnsync
. Esse objeto pode ser usado pelo processo de recebimento para configurar a outra extremidade da fila.- As filas de sincronização não podem transbordar e só podem ter um leitor.
- As filas não sincronizadas podem transbordar e ter muitos leitores, cada um precisa ler os dados a tempo ou perdê-los.
Para mais detalhes sobre a FMQ, consulte Fila de mensagens rápidas (FMQ).