HIDL Java

No Android 8.0, o SO Android foi reestruturado para definir interfaces claras entre a plataforma Android independente do 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ões, que podem estar em Java (descrito abaixo) ou ser interfaces HIDL do lado do cliente e do servidor em C++.

As interfaces HIDL são usadas principalmente em código nativo e, como resultado, o foco delas é a geração automática de código eficiente em C++. No entanto, as interfaces HIDL também precisam estar disponíveis para uso diretamente do Java, já que alguns subsistemas do Android (como a telefonia) têm interfaces HIDL Java.

As páginas desta seção descrevem o front-end 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.

Exemplo de cliente

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

Adicionar bibliotecas

É necessário adicionar dependências à biblioteca de stub HIDL correspondente se você quiser usá-la. Normalmente, essa é 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ê já estiver extraindo dependências dessas bibliotecas, também poderá 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 app de sistema ou fornecedor direcionado ao Android 10 ou mais recente, é possível incluir essas bibliotecas de forma estática. Também é possível usar apenas classes HIDL de JARs personalizados instalados no dispositivo com APIs Java estáveis disponibilizadas usando o mecanismo uses-library atual para apps do sistema. A segunda abordagem economiza espaço no dispositivo. Para mais detalhes, consulte Como implementar a biblioteca Java SDK. Para apps mais antigos, o comportamento antigo é preservado.

A partir do Android 10, versões "superficiais" dessas bibliotecas também estão disponíveis. Elas incluem a classe em questão, mas não incluem 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 todas as interfaces HIDL. Se você estiver criando uma biblioteca que já tem as classes básicas da interface preferida disponíveis como uma dependência, use 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 de gerenciamento de HIDL também não estão mais disponíveis no bootclasspath de apps. Antes, elas eram usadas como API oculta devido ao carregador de classes de delegação do Android. Em vez disso, elas foram movidas para um novo namespace com jarjar, e os apps que as usam (necessariamente apps privados) precisam ter as próprias cópias separadas. Os módulos no caminho de classe de inicialização que usam HIDL precisam usar as variantes rasas dessas bibliotecas Java e adicionar jarjar_rules: ":framework-jarjar-rules" ao Android.bp para usar a versão dessas bibliotecas que existe no caminho de classe de inicialização.

Modificar a origem Java

Há apenas uma versão (@1.0) desse serviço, portanto, esse código recupera apenas essa versão. Consulte Extensões de interface para saber como processar 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(…);

Oferecer um serviço

O código do framework em Java pode precisar fornecer interfaces para receber callbacks assíncronos de HALs.

Para a interface IFooCallback na versão 1.0 do pacote android.hardware.foo, é possível implementar sua interface em Java seguindo estas 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 crie uma nova classe para estender e implementar os métodos abstratos.

Conferir arquivos gerados automaticamente

Para conferir 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 do proxy e os stubs (o proxy e os stubs estão em conformidade com a interface).

O -Lmakefile gera as regras que executam esse comando no momento do build e permite que você inclua android.hardware.foo-V1.0-java e estabeleça links com os 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. O hardware/interfaces pode ser um diretório temporário na árvore de código para que você possa desenvolver um HAL antes de publicá-lo.

Executar um serviço

A HAL fornece a interface IFoo, que precisa fazer callbacks assíncronos para a estrutura pela interface IFooCallback. A interface IFooCallback não é registrada por nome como um serviço detectável. Em vez disso, IFoo precisa conter um método, como setFooCallback(IFooCallback x).

Para configurar o IFooCallback na 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 dispositivo específico o serviço forneça mais recursos implementados na extensão de interface IBetterFoo, conforme abaixo:

interface IFoo {
   ...
};

interface IBetterFoo extends IFoo {
   ...
};

A chamada de código ciente da interface estendida pode usar o método Java castFrom() para converter com segurança a interface de base na 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.
}