Interfaces e pacotes

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 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, apenas os UDTs em types.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.