Interfaces e pacotes

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, o 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 Localização Tipos de interface
android.hardware.* hardware/interfaces/* HAL
android.frameworks.* frameworks/hardware/interfaces/* frameworks/ relacionados
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 instrução de 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 extend explícita extends 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 de import é um mecanismo HIDL para acessar interfaces e tipos de pacotes em outro pacote. Uma instrução de import se preocupa com duas entidades:

  • A entidade importadora, que pode ser um pacote ou uma interface; e
  • A entidade importada , que também pode ser um pacote ou uma interface.

A entidade importadora é determinada pela localização da declaração de import . Quando a declaração está dentro de um pacote types.hal , 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 de 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á preenchido automaticamente com informações do pacote atual. Para valores totalmente qualificados, os seguintes casos de importação são suportados:

  • Importações de pacotes inteiros . 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, os types.hal do pacote e essa interface são importados para a entidade importadora.
    • Um UDT definido em types.hal , então somente esse UDT é importado para a entidade importadora (outros tipos em types.hal não são importados).
  • 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 em types.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 invocá-los, passar handles para eles e/ou herdar deles.

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 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.
  • Um pacote pode adicionar funcionalidade a outro, incorporando sua API inalterada.
  • 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 menor 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 é baseada em (estende) uma interface IFoo na versão 1.2 do pacote original , e uma versão 1.3 do pacote original é criada, IBar versão 4.1 não pode estender a versão 1.3 do IFoo . Em vez disso, IBar versão 4.1 deve estender o IBar versão 4.0, que está vinculado ao IFoo versão 1.2. IBar versão 5.0 pode estender o IFoo versão 1.3, se desejado.

Extensões de interface não implicam dependência de biblioteca ou inclusão de cross-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 fornecedores

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

Controle de versão

Os pacotes são versionados e as interfaces têm a versão de seu pacote. As versões são expressas em dois inteiros, major . menor .

  • 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.
  • Versões secundárias são compatíveis com versões anteriores. Incrementar o número menor indica que a versão mais recente é totalmente compatível com a versão anterior. Novas estruturas de dados e métodos podem ser adicionados, mas nenhuma estrutura de dados existente ou assinaturas de métodos podem ser alteradas.

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 versões e extensões de fornecedores, consulte HIDL Versioning .

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 HIDL Versioning , 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á 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 obter mais definições, consulte Terminologia HIDL.

Cada arquivo pode ser encontrado a partir do mapeamento da 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 deve estar localizado em $ANDROID_BUILD_TOP/some/device/independent/path/interfaces/foo/1.0/IFoo.hal .

Na prática, é recomendado para um fornecedor ou OEM chamado awesome colocar suas interfaces padrão em vendor.awesome . Após a seleção de um caminho de pacote, ele não deve ser alterado, pois é inserido 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 facilita muito o versionamento de interfaces ).

A raiz do pacote deve ter um arquivo 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 vão 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 teste máximo de código em diferentes dispositivos e casos de uso.