HIDL Java

No Android 8.0, o sistema operacional Android foi reprojetado para definir interfaces claras entre a plataforma Android independente de dispositivo e o código específico do dispositivo e do fornecedor. O Android já definiu muitas dessas interfaces na forma de interfaces HAL, definidas como cabeçalhos C em hardware/libhardware . O HIDL substituiu essas interfaces HAL por interfaces estáveis ​​e com versão, que podem estar em Java (descrito abaixo) ou ser interfaces HIDL do lado do cliente e do servidor em C++ .

As interfaces HIDL devem ser usadas principalmente a partir de código nativo e, como resultado, o HIDL é focado na geração automática de código eficiente em C++. No entanto, as interfaces HIDL também devem estar disponíveis para uso diretamente do Java, pois alguns subsistemas Android (como Telefonia) possuem interfaces Java HIDL.

As páginas nesta seção descrevem o frontend Java para interfaces HIDL, detalham como criar, registrar e usar serviços e explicam como HALs e clientes HAL escritos em Java interagem com o sistema HIDL RPC.

Ser cliente

Este é um exemplo de um cliente para uma interface IFoo no pacote android.hardware.foo@1.0 que está registrado como nome de serviço default e um serviço adicional com o nome de serviço customizado second_impl .

Adicionando bibliotecas

Você precisa adicionar dependências na biblioteca de stub HIDL correspondente se quiser usá-la. Normalmente, esta é uma biblioteca estática:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Se você sabe que já está recebendo dependências dessas bibliotecas, também pode usar a vinculação compartilhada:

// in Android.bp
libs: [ "android.hardware.foo-V1.0-java", ],
// in Android.mk
LOCAL_JAVA_LIBRARIES += android.hardware.foo-V1.0-java

Considerações adicionais para adicionar bibliotecas no Android 10

Se você tiver um aplicativo de sistema ou fornecedor direcionado ao Android 10 ou superior, poderá incluir essas bibliotecas estaticamente. Você também pode usar (somente) classes HIDL de JARs personalizados instalados no dispositivo com APIs Java estáveis ​​disponibilizadas usando o mecanismo uses-library existente para aplicativos do sistema. A última abordagem economiza espaço no dispositivo. Para obter mais detalhes, consulte Implementando a Biblioteca Java SDK . Para aplicativos mais antigos, o comportamento antigo é preservado.

A partir do Android 10, também estão disponíveis versões "rasas" dessas bibliotecas. Isso inclui a classe em questão, mas não inclui nenhuma das classes dependentes. Por exemplo, android.hardware.foo-V1.0-java-shallow inclui classes no pacote foo, mas não inclui classes em android.hidl.base-V1.0-java , que contém a classe base de todos os HIDL interfaces. Se você estiver criando uma biblioteca que já tenha as classes base da interface preferencial disponíveis como dependência, poderá usar o seguinte:

// in Android.bp
static_libs: [ "android.hardware.foo-V1.0-java-shallow", ],
// in Android.mk
LOCAL_STATIC_JAVA_LIBRARIES += android.hardware.foo-V1.0-java-shallow

As bibliotecas de base e gerenciador HIDL também não estão mais disponíveis no caminho de classe de inicialização para aplicativos (anteriormente, às vezes elas eram usadas como API oculta, devido ao carregador de classe delegado primeiro do Android). Em vez disso, eles foram movidos para um novo namespace com jarjar e os aplicativos que usam esses (necessariamente aplicativos priv) devem ter suas próprias cópias separadas. Módulos no caminho de classe de inicialização usando HIDL devem usar as variantes superficiais dessas bibliotecas Java e adicionar jarjar_rules: ":framework-jarjar-rules" ao seu Android.bp para usar a versão dessas bibliotecas que existe no caminho de classe de inicialização.

Modificando sua fonte Java

Há apenas uma versão ( @1.0 ) desse serviço, portanto, esse código recupera apenas essa versão. Consulte as extensões de interface para saber como lidar com várias versões diferentes do serviço.

import android.hardware.foo.V1_0.IFoo;
...
// retry to wait until the service starts up if it is in the manifest
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IFoo anotherServer = IFoo.getService("second_impl", true /* retry */);
server.doSomething(…);

Prestando um serviço

O código de estrutura em Java pode precisar servir interfaces para receber retornos de chamada assíncronos de HALs.

Para a interface IFooCallback na versão 1.0 do pacote android.hardware.foo , você pode implementar sua interface em Java usando as seguintes etapas:

  1. Defina sua interface em HIDL.
  2. Abra /tmp/android/hardware/foo/IFooCallback.java como referência.
  3. Crie um novo módulo para sua implementação Java.
  4. Examine a classe abstrata android.hardware.foo.V1_0.IFooCallback.Stub e escreva uma nova classe para estendê-la e implementar os métodos abstratos.

Visualizando arquivos gerados automaticamente

Para visualizar os arquivos gerados automaticamente, execute:

hidl-gen -o /tmp -Ljava \
  -randroid.hardware:hardware/interfaces \
  -randroid.hidl:system/libhidl/transport android.hardware.foo@1.0

Esses comandos geram o diretório /tmp/android/hardware/foo/1.0 . Para o arquivo hardware/interfaces/foo/1.0/IFooCallback.hal , isso gera o arquivo /tmp/android/hardware/foo/1.0/IFooCallback.java , que encapsula a interface Java, o código proxy e os stubs (ambos proxy e stubs estão em conformidade com a interface).

-Lmakefile gera as regras que executam esse comando em tempo de compilação e permitem incluir android.hardware.foo-V1.0-java e vincular aos arquivos apropriados. Um script que faz isso automaticamente para um projeto cheio de interfaces pode ser encontrado em hardware/interfaces/update-makefiles.sh . Os caminhos neste exemplo são relativos; hardware/interfaces pode ser um diretório temporário em sua árvore de código para permitir que você desenvolva um HAL antes de publicá-lo.

Executando um serviço

O HAL fornece a interface IFoo , que deve fazer retornos de chamada assíncronos para a estrutura pela interface IFooCallback . A interface IFooCallback não é registrada pelo nome como um serviço detectável; em vez disso, o IFoo deve conter um método como setFooCallback(IFooCallback x) .

Para configurar o IFooCallback da versão 1.0 do pacote android.hardware.foo , adicione android.hardware.foo-V1.0-java a Android.mk . O código para executar o serviço é:

import android.hardware.foo.V1_0.IFoo;
import android.hardware.foo.V1_0.IFooCallback.Stub;
....
class FooCallback extends IFooCallback.Stub {
    // implement methods
}
....
// Get the service from which you will be receiving callbacks.
// This also starts the threadpool for your callback service.
IFoo server = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
....
// This must be a persistent instance variable, not local,
//   to avoid premature garbage collection.
FooCallback mFooCallback = new FooCallback();
....
// Do this once to create the callback service and tell the "foo-bar" service
server.setFooCallback(mFooCallback);

Extensões de interface

Supondo que um determinado serviço implemente a interface IFoo em todos os dispositivos, é possível que em um determinado dispositivo o serviço forneça recursos adicionais implementados na extensão de interface IBetterFoo , conforme a seguir:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

Chamar o código ciente da interface estendida pode usar o método Java castFrom() para converter com segurança a interface base para a interface estendida:

IFoo baseService = IFoo.getService(true /* retry */); // throws NoSuchElementException if not available
IBetterFoo extendedService = IBetterFoo.castFrom(baseService);
if (extendedService != null) {
  // The service implements the extended interface.
} else {
  // The service implements only the base interface.
}