O Android 8 reprojeta o SO Android para definir interfaces claras entre a
plataforma Android independente do dispositivo e o código específico do dispositivo e do fornecedor.
O Android já define muitas dessas interfaces na forma de interfaces HAL,
definidas como cabeçalhos C em hardware/libhardware
. O HIDL substitui essas
interfaces HAL por interfaces estáveis e versionadas, que podem ser interfaces HIDL do lado do cliente e
do servidor em C++ (descritas abaixo) ou
Java.
As páginas desta seção descrevem implementações em C++ de interfaces HIDL,
incluindo detalhes sobre os arquivos gerados automaticamente dos arquivos .hal
HIDL pelo compilador hidl-gen
, como esses arquivos são empacotados e
como integrá-los ao código C++ que os usa.
Implementações de cliente e servidor
As interfaces HIDL têm implementações de cliente e servidor:
- Um cliente de uma interface HIDL é o código que usa a interface chamando métodos nela.
- Um servidor é uma implementação de uma interface HIDL que recebe chamadas de clientes e retorna resultados (se necessário).
Na transição de HALs libhardware
para HALs HIDL, a implementação
HAL se torna o servidor, e o processo que chama a HAL se torna
o cliente. As implementações padrão podem servir HALs de passagem e
vinculadas e podem mudar com o tempo:
Figura 1. Progresso do desenvolvimento para HALs legados.
Criar o cliente HAL
Comece incluindo as bibliotecas HAL no makefile:
- Make:
LOCAL_SHARED_LIBRARIES += android.hardware.nfc@1.0
- Soong:
shared_libs: [ …, android.hardware.nfc@1.0 ]
Em seguida, inclua os arquivos de cabeçalho HAL:
#include <android/hardware/nfc/1.0/IFoo.h> … // in code: sp<IFoo> client = IFoo::getService(); client->doThing();
Criar o servidor HAL
Para criar a implementação do HAL, você precisa ter os arquivos .hal
que representam o HAL e já ter gerado arquivos de make para o HAL usando
-Lmakefile
ou -Landroidbp
no hidl-gen
.
(./hardware/interfaces/update-makefiles.sh
faz isso para arquivos HAL
internos e é uma boa referência). Ao transferir por HALs de
libhardware
, é possível fazer muito desse trabalho com facilidade usando o c2hal.
Para criar os arquivos necessários para implementar a HAL:
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 o HAL funcione no modo de transferência, é necessário ter
a função HIDL_FETCH_IModuleName
em
/(system|vendor|...)/lib(64)?/hw/android.hardware.package@3.0-impl(OPTIONAL_IDENTIFIER).so
,
em que OPTIONAL_IDENTIFIER é uma string que identifica a implementação
de transferência. Os requisitos do modo de transferência são atendidos automaticamente pelos comandos acima, que também criam o destino android.hardware.nfc@1.0-impl
, mas qualquer extensão pode ser usada. Por exemplo,
android.hardware.nfc@1.0-impl-foo
usa -foo
para
se diferenciar.
Se um HAL for uma versão secundária ou uma extensão de outro
HAL, o HAL de base será usado para nomear esse binário. Por exemplo,
as implementações de android.hardware.graphics.mapper@2.1
ainda precisam
estar em um binário chamado
android.hardware.graphics.mapper@2.0-impl(OPTIONAL_IDENTIFIER)
.
Normalmente, o OPTIONAL_IDENTIFIER aqui inclui a versão
real do HAL. Ao nomear o binário dessa forma, os clientes 2.0 podem extraí-lo diretamente,
e os clientes 2.1 podem upcastar a implementação.
Em seguida, preencha os stubs com a funcionalidade e configure um daemon. Exemplo de código de daemon (com suporte a transmissão):
#include <hidl/LegacySupport.h> int main(int /* argc */, char* /* argv */ []) { return defaultPassthroughServiceImplementation<INfc>("nfc"); }
defaultPassthroughServiceImplementation
chama
dlopen()
para a biblioteca -impl
fornecida e a fornece como
um serviço vinculado. Exemplo de código de daemon (para serviço puro de vinculação):
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 }
Esse daemon geralmente fica em $PACKAGE + "-service-suffix"
(por
exemplo, android.hardware.nfc@1.0-service
), mas pode estar em qualquer lugar.
A sepolicy de uma classe específica
de HALs é o atributo hal_<module>
(por exemplo,
hal_nfc)
. Esse atributo precisa ser aplicado ao daemon que executa um
HAL específico. Se o mesmo processo atender a vários HALs, vários atributos
poderão ser aplicados a ele.