HIDL Java

No Android 8.0, o sistema operacional Android foi reprojetado 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 versionadas, que podem estar em Java (descritas abaixo) ou ser interfaces HIDL do lado do cliente e do servidor em C++ .

As interfaces HIDL destinam-se a ser usadas principalmente a partir de código nativo e, como resultado, o HIDL está 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 um cliente

Este é um exemplo de 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 personalizado second_impl .

Adicionando bibliotecas

Você precisa adicionar dependências na biblioteca 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á extraindo dependências dessas bibliotecas, também pode usar a ligaçã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 (apenas) classes HIDL de JARs customizados 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, versões “superficiais” dessas bibliotecas também estão disponíveis. 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 estiver criando uma biblioteca que já tenha as classes base da interface preferencial disponíveis como dependência, você 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 base e gerenciadoras HIDL também não estão mais disponíveis no caminho de classe de inicialização para aplicativos (anteriormente, elas às vezes 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 os usam (necessariamente aplicativos privados) devem ter suas próprias cópias separadas. Os 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

Existe apenas uma versão ( @1.0 ) deste serviço, portanto este 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(…);

Fornecendo um serviço

O código da 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, em seguida, 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 do proxy e os stubs (ambos proxy e stubs estão em conformidade com a interface).

-Lmakefile gera as regras que executam este comando no momento da compilação e permite 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 por meio da interface IFooCallback . A interface IFooCallback não está registrada pelo nome como um serviço detectável; em vez disso, IFoo deve conter um método como setFooCallback(IFooCallback x) .

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

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