O HIDL é criado com base em interfaces, um tipo abstrato usado em linguagens orientadas a objetos para definir comportamentos. Cada interface faz parte de um pacote.
Pacotes
Os nomes dos pacotes podem ter subníveis, como package.subpackage
. O
diretório raiz para pacotes HIDL publicados é hardware/interfaces
ou vendor/vendorName
(por exemplo, vendor/google
para dispositivos
Pixel). O nome do pacote forma um ou mais subdiretórios no diretório
raiz. Todos os arquivos que definem um pacote estão no mesmo diretório. Por exemplo,
package android.hardware.example.extension.light@2.0
pode ser encontrado
em hardware/interfaces/example/extension/light/2.0
.
A tabela a seguir lista os prefixos e locais dos pacotes:
Prefixo do pacote | Local | Tipos de interface |
---|---|---|
android.hardware.* |
hardware/interfaces/* |
HAL |
android.frameworks.* |
frameworks/hardware/interfaces/* |
frameworks/ related |
android.system.* |
system/hardware/interfaces/* |
sistema/ relacionado |
android.hidl.* |
system/libhidl/transport/* |
core |
O diretório do pacote contém arquivos com a extensão .hal
. Cada
arquivo precisa conter uma instrução package
que nomeie o pacote e
a versão do arquivo. O arquivo types.hal
, se presente, não
define uma interface, mas sim tipos de dados acessíveis para todas as
interfaces no pacote.
Definição da interface
Além de types.hal
, todos os outros arquivos .hal
definem
uma interface. Uma interface geralmente é definida da seguinte maneira:
interface IBar extends IFoo { // IFoo is another interface // embedded types struct MyStruct {/*...*/}; // interface methods create(int32_t id) generates (MyStruct s); close(); };
Uma interface sem uma declaração extends
explícita estende-se implicitamente
de android.hidl.base@1.0::IBase
(semelhante a
java.lang.Object
em Java). A interface IBase, importada implicitamente, declara vários métodos reservados que não podem ser
redeclarados em interfaces definidas pelo usuário ou usados de outra forma. Esses métodos
incluem:
ping
interfaceChain
interfaceDescriptor
notifySyspropsChanged
linkToDeath
unlinkToDeath
setHALInstrumentation
getDebugInfo
debug
getHashChain
Processo de importação
A instrução import
é um mecanismo de HIDL para acessar interfaces
e tipos de pacote em outro pacote. Uma instrução import
se preocupa com duas entidades:
- A entidade import, que pode ser um pacote ou uma interface
- A entidade importada, que pode ser um pacote ou uma interface
A entidade importadora é determinada pelo local da
instrução import
. Quando a instrução está dentro de um
types.hal
de um pacote, o que está sendo importado fica visível para todo o pacote.
Essa é uma importação no nível do pacote. Quando a instrução está dentro de um
arquivo de interface, a entidade de importação é a própria interface. Essa é uma importação
no nível da interface.
A entidade importada é determinada pelo valor após a palavra-chave import
. O valor não precisa ser um nome totalmente qualificado. Se um componente for
omitido, ele será preenchido automaticamente com informações do pacote atual.
Para valores totalmente qualificados, os seguintes casos de importação são aceitos:
- Importações de pacotes inteiros. Se o valor for um nome de pacote e uma versão (sintaxe descrita abaixo), o pacote inteiro será importado para a entidade de importação.
- Importações parciais. Se o valor for:
- Uma interface, o
types.hal
do pacote e essa interface são importados para a entidade importadora. - Uma UDT definida em
types.hal
, então apenas essa UDT é importada para a entidade importadora (outros tipos emtypes.hal
não são importados).
- Uma interface, o
- Importações somente de tipos. Se o valor usar a sintaxe de uma
importação parcial descrita acima, mas com a palavra-chave
types
em vez de um nome de interface, apenas os UDTs emtypes.hal
do pacote designado serão importados.
A entidade importadora tem acesso a uma combinação de:
- As UDTs comuns do pacote importado definidas em
types.hal
; - As interfaces do pacote importado (para uma importação de pacote inteiro) ou a interface especificada (para uma importação parcial) para invocá-las, transmitindo identificadores para elas e/ou herdando delas.
A instrução de importação usa a sintaxe de nome de tipo totalmente qualificado para fornecer o nome e a versão do pacote ou da interface que está sendo importado:
import android.hardware.nfc@1.0; // import a whole package import android.hardware.example@1.0::IQuux; // import an interface and types.hal import android.hardware.example@1.0::types; // import just types.hal
Herança de interface
Uma interface pode ser uma extensão de uma interface definida anteriormente. As extensões podem ser de três tipos:
- A interface pode adicionar funcionalidade a outra, incorporando a API sem alterações.
- O pacote pode adicionar funcionalidade a outro, incorporando a API sem alterações.
- A interface pode importar tipos de um pacote ou de uma interface específica.
Uma interface pode estender apenas uma outra interface (sem herança múltipla).
Cada interface em um pacote com um número de versão secundária diferente de zero precisa estender uma
interface na versão anterior do pacote. Por exemplo, se uma interface
IBar
na versão 4.0 do pacote derivative
for baseada em
(estender) uma interface IFoo
na versão 1.2 do pacote
original
e uma versão 1.3 do pacote original
for
criada, a IBar
na versão 4.1 não poderá estender a versão 1.3 de
IFoo
. Em vez disso, a versão 4.1 da IBar
precisa estender
a versão 4.0 da IBar
, que está vinculada à versão 1.2 da IFoo
.
A versão 5.0 da IBar
pode estender a versão 1.3 da IFoo
, se
desejado.
As extensões de interface não implicam dependência de biblioteca ou inclusão entre HALs no código gerado. Elas simplesmente importam a estrutura de dados e as definições de método no nível HIDL. Todos os métodos em um HAL precisam ser implementados nesse HAL.
Extensões do fornecedor
Em alguns casos, as extensões do fornecedor são implementadas como uma subclasse do objeto base que representa a interface principal que elas estendem. O mesmo objeto é registrado com o nome e a versão da HAL de base e com o nome e a versão da HAL (do fornecedor) da extensão.
Controle de versões
Os pacotes têm versões, e as interfaces têm a versão do pacote. As versões são expressas em dois números inteiros, major.minor.
- As versões principais não são compatíveis com versões anteriores. Incrementar o número da versão principal redefine o número da versão secundária para 0.
- As versões secundárias são compatíveis com versões anteriores. O incremento do número secundário indica que a versão mais recente é totalmente compatível com a versão anterior. É possível adicionar novas estruturas e métodos de dados, mas não é possível mudar estruturas de dados ou assinaturas de métodos existentes.
Várias versões principais ou secundárias de um HAL podem estar presentes em um dispositivo simultaneamente. No entanto, uma versão secundária é preferível a uma versão principal, porque o código do cliente que funciona com uma interface de versão secundária anterior também funciona com versões secundárias posteriores dessa mesma interface. Para mais detalhes sobre o controle de versões e as extensões do fornecedor, consulte Controle de versões do HIDL.
Resumo do layout da interface
Esta seção resume como gerenciar um pacote de interface HIDL (como
hardware/interfaces
) e consolida as informações apresentadas
em toda a seção de HIDL. Antes de ler, confira se você conhece os conceitos de
versionamento de HIDL,
hashing com
hidl-gen, os detalhes de como trabalhar com
HIDL em geral e as seguintes definições:
Termo | Definição |
---|---|
Interface binária do aplicativo (ABI) | Interface de programação de aplicativos e todas as vinculações binárias necessárias. |
nome totalmente qualificado (fqName) | Nome para distinguir um tipo de hidl. Exemplo:
android.hardware.foo@1.0::IFoo . |
pacote | Pacote que contém uma interface e tipos HIDL. Exemplo:
android.hardware.foo@1.0 . |
raiz do pacote | Pacote raiz que contém as interfaces HIDL. Exemplo: a interface HIDL
android.hardware está na raiz do pacote
android.hardware.foo@1.0 . |
caminho raiz do pacote | Local na árvore de origem do Android para onde uma raiz de pacote é mapeada. |
Para mais definições, consulte a terminologia da HIDL.
Todos os arquivos podem ser encontrados no mapeamento raiz do pacote e no nome totalmente qualificado.
As raízes do pacote são especificadas para hidl-gen
como o argumento
-r android.hardware:hardware/interfaces
. Por exemplo, se o
pacote for vendor.awesome.foo@1.0::IFoo
e hidl-gen
for enviado -r vendor.awesome:some/device/independent/path/interfaces
,
o arquivo de interface precisa estar localizado em
$ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal
.
Na prática, é recomendável que um fornecedor ou OEM chamado awesome
coloque as interfaces padrão em vendor.awesome
. Depois que um caminho
de pacote é selecionado, ele não pode ser alterado, porque ele é incorporado à ABI da
interface.
O mapeamento do caminho do pacote precisa ser exclusivo
Por exemplo, se você tiver -rsome.package:$PATH_A
e
-rsome.package:$PATH_B
, $PATH_A
precisa ser igual a
$PATH_B
para um diretório de interface consistente. Isso também facilita
a
criação de versões de interfaces.
A raiz do pacote precisa ter um arquivo de controle de versão
Se você criar um caminho de pacote, como
-r vendor.awesome:vendor/awesome/interfaces
, também precisará
criar o arquivo
$ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt
, que
deve conter hashes de interfaces feitas usando a opção -Lhash
em
hidl-gen
. Isso é discutido extensivamente em
Hashing com
hidl-gen.
As interfaces vão para locais independentes do dispositivo
Na prática, recomendamos compartilhar interfaces entre as filiais. Isso permite o máximo de reutilização e teste de código em diferentes dispositivos e casos de uso.