O HIDL é construído em torno de 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 prefixos e locais de pacotes:
Prefixo do pacote | Localização | Tipos de interface |
---|---|---|
android.hardware.* | hardware/interfaces/* | HAL |
android.frameworks.* | frameworks/hardware/interfaces/* | estruturas/relacionadas |
android.system.* | system/hardware/interfaces/* | sistema/relacionado |
android.hidl.* | system/libhidl/transport/* | essencial |
O diretório do pacote contém arquivos com extensão .hal
. Cada arquivo deve conter uma declaração package
nomeando o pacote e a versão da qual o arquivo faz parte. O arquivo types.hal
, se presente, não define uma interface, mas define tipos de dados acessíveis a todas as interfaces do pacote.
Definição de interface
Além de types.hal
, todos os outros arquivos .hal
definem uma interface. Uma interface normalmente é definida da seguinte forma:
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 se estende 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 devem e não podem ser redeclarados em interfaces definidas pelo usuário ou usadas de outra forma. Esses métodos incluem:
-
ping
-
interfaceChain
-
interfaceDescriptor
-
notifySyspropsChanged
-
linkToDeath
-
unlinkToDeath
-
setHALInstrumentation
-
getDebugInfo
-
debug
-
getHashChain
Importando
A instrução import
é um mecanismo HIDL para acessar interfaces e tipos de pacotes em outro pacote. Uma declaração import
diz respeito a duas entidades:
- A entidade importadora , que pode ser um pacote ou uma interface; e
- A entidade import ed , que também pode ser um pacote ou uma interface.
A entidade importadora é determinada pela localização da declaração import
. Quando a instrução está dentro do types.hal
de um pacote, o que está sendo importado fica visível por todo o pacote; esta é uma importação em nível de pacote . Quando a instrução está dentro de um arquivo de interface, a entidade importadora é a própria interface; esta é uma importação em nível de 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á automaticamente preenchido com informações do pacote atual. Para valores totalmente qualificados, há suporte para os seguintes casos de importação:
- Importações de pacote inteiro . Se o valor for um nome de pacote e uma versão (sintaxe descrita abaixo), todo o pacote será importado para a entidade importadora.
- Importações parciais . Se o valor for:
- Uma interface, o
types.hal
do pacote e essa interface são importados para a entidade importadora. - Um UDT definido em
types.hal
, somente esse UDT é importado 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, somente os UDTs emtypes.hal
do pacote designado serão importados.
A entidade importadora obtém acesso a uma combinação de:
- Os UDTs comuns do pacote importado definidos em
types.hal
; - As interfaces do pacote importado (para uma importação de pacote inteiro) ou interface especificada (para uma importação parcial) para fins de invocá-los, passar identificadores para eles e/ou herdar deles.
A instrução import usa a sintaxe full-qualified-type-name para fornecer o nome e a versão do pacote ou interface que está sendo importada:
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 previamente definida. As extensões podem ser de um dos três tipos a seguir:
- Interface pode adicionar funcionalidade a outra, incorporando sua API inalterada.
- O pacote pode adicionar funcionalidade a outro, incorporando sua API inalterada.
- 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 deve 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 (estende) uma interface IFoo
na versão 1.2 do pacote original
e uma versão 1.3 do pacote original
for criada, a versão 4.1 IBar
não poderá estender a versão 1.3 do IFoo
. Em vez disso, IBar
versão 4.1 deve estender IBar
versão 4.0, que está vinculado ao IFoo
versão 1.2. A versão 5.0 IBar
pode estender a versão 1.3 IFoo
, se desejado.
As extensões de interface não implicam dependência de biblioteca ou inclusão cruzada de HAL no código gerado – elas simplesmente importam a estrutura de dados e as definições de método no nível HIDL. Cada método em um HAL deve ser implementado nesse HAL.
Extensões de fornecedor
Em alguns casos, as extensões do fornecedor serão implementadas como uma subclasse do objeto base que representa a interface principal que elas estendem. O mesmo objeto será registrado sob o nome e versão HAL base e sob o nome e versão HAL da extensão (fornecedor).
Versionamento
Os pacotes são versionados e as interfaces possuem a versão de seu pacote. As versões são expressas em dois números inteiros, major . menor .
- As versões principais não são compatíveis com versões anteriores. Aumentar o número da versão principal redefine o número da versão secundária para 0.
- Versões secundárias são compatíveis com versões anteriores. Aumentar o número menor indica que a versão mais recente é totalmente compatível com versões anteriores. Novas estruturas de dados e métodos podem ser adicionados, mas nenhuma estrutura de dados ou assinatura de método existente pode ser alterada.
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 deve ser preferida a uma versão principal porque o código do cliente que funciona com uma interface de versão secundária anterior também funcionará com versões secundárias posteriores dessa mesma interface. Para obter mais detalhes sobre versionamento e extensões de fornecedores, consulte Versionamento 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 HIDL. Antes de ler, certifique-se de estar familiarizado com o Versionamento HIDL , os conceitos de hash em Hashing com hidl-gen , os detalhes de como trabalhar com HIDL em geral e as seguintes definições:
Prazo | Definição |
---|---|
Interface Binária do Aplicativo (ABI) | Interface de programação de aplicativos + quaisquer ligações binárias necessárias. |
Nome totalmente qualificado (fqName) | Nome para distinguir um tipo hidl. Exemplo: android.hardware.foo@1.0::IFoo . |
Pacote | Pacote contendo 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á no pacote root android.hardware.foo@1.0 . |
Caminho raiz do pacote | Local na árvore de origem do Android para onde a raiz do pacote é mapeada. |
Para obter mais definições, consulte Terminologia HIDL.
Cada arquivo pode ser encontrado no mapeamento raiz do pacote e seu 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
, então o arquivo de interface deverá estar localizado em $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal
.
Na prática, é recomendado que um fornecedor ou OEM chamado awesome
coloque suas interfaces padrão em vendor.awesome
. Após a seleção de um caminho de pacote, ele não deve ser alterado, pois isso está incluído na ABI da interface.
O mapeamento do caminho do pacote deve ser exclusivo
Por exemplo, se você tiver -rsome.package:$PATH_A
e -rsome.package:$PATH_B
, $PATH_A
deve ser igual a $PATH_B
para um diretório de interface consistente (isso também torna o versionamento de interfaces muito mais fácil).
A raiz do pacote deve ter um arquivo de controle de versão
Se você criar um caminho de pacote como -r vendor.awesome:vendor/awesome/interfaces
, você também deve criar o arquivo $ANDROID_BUILD_TOP/vendor/awesome/interfaces/current.txt
, que deve conter hashes de interfaces feitas usando o -Lhash
opção em hidl-gen
(isso é discutido extensivamente em Hashing com hidl-gen ).
As interfaces ficam em locais independentes do dispositivo
Na prática, é recomendado compartilhar interfaces entre filiais. Isso permite a reutilização máxima de código e o máximo teste de código em diferentes dispositivos e casos de uso.