API Instrument Cluster

Use a API Instrument Cluster (uma API do Android) para mostrar apps de navegação, incluindo o Google Maps, em uma tela secundária de um carro, como atrás do volante no painel de instrumentos. Esta página descreve como criar um serviço para controlar essa tela secundária e integrar o serviço ao CarService para que os apps de navegação possam mostrar uma interface do usuário.

Terminologia

Os termos a seguir são usados nesta página.

CarInstrumentClusterManager
Uma instância de CarManager que permite que apps externos iniciem uma atividade no cluster de instrumentos e recebam callbacks quando o cluster de instrumentos estiver pronto para mostrar atividades.
CarManager
Classe de base de todos os gerenciadores usados por apps externos para interagir com serviços específicos do carro implementados pelo CarService.
CarService
Serviço da plataforma Android que fornece comunicação entre apps externos (incluindo o Google Maps) e recursos específicos do carro, como o acesso ao cluster de instrumentos.
Destino
O destino final para onde o veículo vai navegar.
Horário previsto de chegada (HEC)
O horário previsto de chegada a um destino.
Unidade principal (HU)
Unidade computacional principal incorporada a um carro. A HU executa todo o código do Android e está conectada à tela central do carro.
Cluster de instrumentos
Tela secundária localizada atrás do volante e entre os instrumentos do carro. Pode ser uma unidade computacional independente conectada à HU pela rede interna do carro (CAN bus) ou uma tela secundária anexada à HU.
InstrumentClusterRenderingService
Classe base para o serviço usado para interagir com a tela do cluster de instrumentos. Os OEMs precisam fornecer uma extensão dessa classe que interaja com o hardware específico do OEM.
App KitchenSink
Teste o app incluído no Android Automotive.
Rota
Um caminho específico que um veículo percorre para chegar a um destino.
Serviço singleton
Um serviço do Android com o atributo android:singleUser. Em qualquer momento, no máximo uma instância do serviço é executada no sistema Android.

Pré-requisitos

Antes de continuar, confira se você tem estes elementos:

  • Ambiente de desenvolvimento do Android. Para configurar o ambiente de desenvolvimento do Android, consulte Requisitos de build.
  • Faça o download do código-fonte do Android. Faça o download da versão mais recente do código-fonte do Android da ramificação pi-car-release (ou mais recente) em https://android.googlesource.com.
  • Unidade principal (HU). Um dispositivo Android capaz de executar o Android 9 (ou mais recente). Esse dispositivo precisa ter uma tela própria e ser capaz de exibir novos builds do Android.
  • O cluster de instrumentos é um dos seguintes:
    • Tela secundária física conectada ao HU. Se o hardware e o kernel do dispositivo oferecem suporte ao gerenciamento de várias telas.
    • Unidade independente. Qualquer unidade computacional conectada à HU por uma conexão de rede, capaz de receber e exibir um stream de vídeo na própria tela.
    • Tela emulada. Durante o desenvolvimento, é possível usar um destes ambientes emulados:
      • Telas secundárias simuladas. Para ativar uma tela secundária simulada em qualquer distribuição do Android AOSP, acesse as configurações de Opções do desenvolvedor no app de sistema Configurações e selecione Simular telas secundárias. Essa configuração é equivalente à conexão de uma tela secundária física, com a limitação de que essa tela é sobreposta à tela principal.
      • Cluster de instrumentos emulados. O emulador do Android incluído no AAOS oferece uma opção para exibir um cluster de instrumentos com ClusterRenderingService.

Arquitetura de integração

Componentes de integração

Qualquer integração da API Instrument Cluster consiste nestes três componentes:

  • CarService
  • Apps de navegação
  • Serviço de cluster de instrumentos OEM

Componentes de integração

CarService

O CarService faz a mediação entre os apps de navegação e o carro, garantindo que apenas um app de navegação esteja ativo em um determinado momento e que apenas apps com a permissão android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL possam enviar dados para o carro.

O CarService inicializa todos os serviços específicos do carro e fornece acesso a esses serviços por meio de uma série de gerenciadores. Para interagir com os serviços, os apps em execução no carro podem acessar esses gerenciadores.

Para a implementação do cluster de instrumentos, os OEMs automotivos precisam criar uma implementação personalizada do InstrumentClusterRendererService e atualizar o ClusterRenderingService.

Ao renderizar um cluster de instrumentos, durante o processo de inicialização, o CarService lê a chave InstrumentClusterRendererService do ClusterRenderingService para localizar uma implementação de InstrumentClusterService. No AOSP, essa entrada aponta para o serviço de renderização de implementação de cluster de exemplo da API Navigation State:

<string name="instrumentClusterRendererService">
android.car.cluster/.ClusterRenderingService
</string>

O serviço mencionado nesta entrada é inicializado e vinculado a CarService. Quando apps de navegação, como o Google Maps, solicitam um CarInstrumentClusterManager, o CarService fornece um gerenciador que atualiza o estado do cluster de instrumentos do InstrumentClusterRenderingService vinculado. Nesse caso, bound se refere a Serviços do Android.

Serviço de cluster de instrumentos

Os OEMs precisam criar um pacote Android (APK) que contenha uma subclasse de ClusterRenderingService.

Essa classe tem dois propósitos:

  • Oferece uma interface Android e o dispositivo de renderização do cluster de instrumentos (o objetivo desta página).
  • Recebe e renderiza atualizações do estado de navegação, como orientações detalhadas de navegação.

Para o primeiro propósito, as implementações OEM de InstrumentClusterRendererService precisam inicializar a tela secundária usada para renderizar informações nas telas da cabine do carro e transmitir essas informações para CarService chamando os métodos InstrumentClusterRendererService.setClusterActivityOptions() e InstrumentClusterRendererService.setClusterActivityState().

Para a segunda função, o serviço Instrument Cluster precisa fornecer uma implementação da interface ClusterRenderingService que recebe eventos de atualização de status de navegação, que são codificados como um eventType e dados de evento codificados em um pacote.

Sequência de integração

O diagrama a seguir ilustra a implementação de um estado de navegação que renderiza atualizações:

Sequência de integração

Nesta ilustração, as cores indicam o seguinte:

  • Amarelo. CarService e CarNavigationStatusManager fornecidos pela plataforma Android. Para saber mais, consulte Carro e CAR_NAVIGATION_SERVICE.
  • Ciano. InstrumentClusterRendererService implementado pelo OEM.
  • Roxo. O app de navegação implementado pelo Google e por desenvolvedores terceirizados.
  • Verde. CarAppFocusManager. Para saber mais, consulte Como usar a API CarAppFocusManager abaixo e CarAppFocusManager.

O fluxo de informações do estado de navegação segue esta sequência:

  1. CarService inicializa o InstrumentClusterRenderingService.
  2. Durante a inicialização, o InstrumentClusterRenderingService atualiza CarService com:
    1. Propriedades de exibição do cluster de instrumentos, como limites não ocultos (mais detalhes sobre limites não ocultos mais tarde).
    2. Opções de atividade necessárias para iniciar atividades na tela do cluster de instrumentos. Para saber mais, consulte ActivityOptions.
  3. Um app de navegação (como o Google Maps para Android Automotive ou qualquer app de mapas com as permissões necessárias):
    1. Obtém um CarAppFocusManager usando a classe Car da car-lib.
    2. Antes de iniciar as direções passo a passo, as chamadas para CarAppFocusManager.requestFocus() transmitem CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION como o parâmetro appType.
  4. O CarAppFocusManager comunica essa solicitação ao CarService. Se concedido, CarService inspeciona o pacote do app de navegação e localiza uma atividade marcada com a categoria android.car.cluster.NAVIGATION.
  5. Se encontrado, o app de navegação usa o ActivityOptions informado pelo InstrumentClusterRenderingService para iniciar a atividade e inclui as propriedades de exibição do cluster de instrumentos como extras na intent.

Integrar a API

A implementação da InstrumentClusterRenderingService precisa:

  • Ser designado como um serviço singleton adicionando o seguinte valor ao AndroidManifest.xml. Isso é necessário para garantir que uma única cópia do serviço de cluster de instrumentos seja executada, mesmo durante a inicialização e a troca de usuários:
    android:singleUser="true"
  • Segure a permissão do sistema BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE. Isso garante que apenas o serviço de renderização do cluster de instrumentos incluído como parte da imagem do sistema Android seja vinculado pelo CarService:
    <uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
    

Implementar o InstrumentClusterRenderingService

Para criar o serviço:

  1. Crie uma classe que se estenda de ClusterRenderingService e adicione uma entrada correspondente ao arquivo AndroidManifest.xml. Essa classe controla a tela do cluster de instrumentos e pode (opcionalmente) renderizar dados da API de estado de navegação.
  2. Durante onCreate(), use esse serviço para inicializar a comunicação com o hardware de renderização. As opções incluem:
    • Determine a tela secundária a ser usada para o conjunto de instrumentos.
    • Crie uma tela virtual para que o app de cluster de instrumentos renderize e transmita a imagem renderizada para uma unidade externa (usando um formato de streaming de vídeo, como H.264).
  3. Quando a tela indicada acima estiver pronta, esse serviço precisará chamar InstrumentClusterRenderingService#setClusterActivityLaunchOptions() para definir o ActivityOptions exato que precisa ser usado para exibir uma atividade no cluster de instrumentos. Use estes parâmetros:
    • category. ClusterRenderingService.
    • ActivityOptions. Uma instância de ActivityOptions que pode ser usada para iniciar uma atividade no cluster de instrumentos. Por exemplo, na implementação de amostra do cluster de instrumentos no AOSP:
      getService().setClusterActivityLaunchOptions(
        CATEGORY_NAVIGATION,
        ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
  4. Quando o cluster de instrumentos estiver pronto para mostrar atividades, esse serviço precisará invocar InstrumentClusterRenderingService#setClusterActivityState(). Use estes parâmetros:
    • category ClusterRenderingService.
    • Pacote state gerado com ClusterRenderingService. Forneça estes dados:
      • visible Especifica o cluster de instrumentos como visível e pronto para mostrar conteúdo.
      • unobscuredBounds Um retângulo que define a área dentro da tela do cluster de instrumentos em que é seguro exibir conteúdo. Por exemplo, áreas cobertas por mostradores e indicadores.
  5. Substitua o método Service#dump() e informe informações de status úteis para depuração (consulte dumpsys para mais informações).

Exemplo de implementação do InstrumentClusterRenderingService

O exemplo a seguir descreve uma implementação de InstrumentClusterRenderingService, que cria um VirtualDisplay para apresentar o conteúdo do cluster de instrumentos em uma tela física remota.

Como alternativa, esse código pode transmitir o displayId de uma tela secundária física conectada ao HU, se for sabido que uma está disponível.

/**
* Sample {@link InstrumentClusterRenderingService} implementation
*/
public class SampleClusterServiceImpl extends InstrumentClusterRenderingService {
   // Used to retrieve or create displays
   private final DisplayManager mDisplayManager;
   // Unique identifier for the display to be used for instrument
   // cluster
   private final String mUniqueId = UUID.randomUUID().toString();
   // Format of the instrument cluster display
   private static final int DISPLAY_WIDTH = 1280;
   private static final int DISPLAY_HEIGHT = 720;
   private static final int DISPLAY_DPI = 320;
   // Area not covered by instruments
   private static final int DISPLAY_UNOBSCURED_LEFT = 40;
   private static final int DISPLAY_UNOBSCURED_TOP = 0;
   private static final int DISPLAY_UNOBSCURED_RIGHT = 1200;
   private static final int DISPLAY_UNOBSCURED_BOTTOM = 680;
   @Override
   public void onCreate() {
      super.onCreate();
      // Create a virtual display to render instrument cluster activities on
      mDisplayManager = getSystemService(DisplayManager.class);
      VirtualDisplay display = mDisplayManager.createVirtualDisplay(
          mUniqueId, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_DPI, null,
          0 /* flags */, null, null);
      // Do any additional initialization (e.g.: start a video stream
      // based on this virtual display to present activities on a remote
      // display).
      onDisplayReady(display.getDisplay());
}
private void onDisplayReady(Display display) {
    // Report activity options that should be used to launch activities on
    // the instrument cluster.
    String category = CarInstrumentClusterManager.CATEGORY_NAVIGATION;
    ActionOptions options = ActivityOptions.makeBasic()
        .setLaunchDisplayId(display.getDisplayId());
    setClusterActivityOptions(category, options);
    // Report instrument cluster state.
    Rect unobscuredBounds = new Rect(DISPLAY_UNOBSCURED_LEFT,
        DISPLAY_UNOBSCURED_TOP, DISPLAY_UNOBSCURED_RIGHT,
        DISPLAY_UNOBSCURED_BOTTOM);
    boolean visible = true;
    ClusterActivityState state = ClusterActivityState.create(visible,
       unobscuredBounds);
    setClusterActivityState(category, options);
  }
}

Usar a API CarAppFocusManager

A API CarAppFocusManager oferece um método chamado getAppTypeOwner(), que permite que o serviço de cluster escrito por OEMs saiba qual app de navegação tem o foco de navegação em um determinado momento. Os OEMs podem usar o método CarAppFocusManager#addFocusListener() atual e depois usar getAppTypeOwner() para saber qual app está em foco. Com essas informações, os OEMs podem:

  • Mudar a atividade mostrada no cluster para a atividade do cluster fornecida pelo app de navegação que está com o foco.
  • Pode detectar se o app de navegação focado tem uma atividade de cluster ou não. Se o app de navegação principal não tiver uma atividade de cluster ou se essa atividade estiver desativada, os OEMs poderão enviar esse sinal para o DIM do carro para que a faceta de navegação do cluster seja totalmente ignorada.

Use CarAppFocusManager para definir e detectar o foco atual do app, como a navegação ativa ou um comando de voz. Normalmente, apenas uma instância desse app está ativamente executada (ou focada) no sistema.

Use o método CarAppFocusManager#addFocusListener(..) para detectar mudanças no foco do app:

import android.car.CarAppFocusManager;

...

Car car = Car.createCar(this);
mAppFocusManager = (CarAppFocusManager)car.getCarManager(Car.APP_FOCUS_SERVICE);
mAppFocusManager.addFocusListener(this, CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);

...

public void onAppFocusChanged(int appType, boolean active) {
    // Use the CarAppFocusManager#getAppTypeOwner(appType) method call
    // to retrieve a list of active package names
}

Use o método CarAppFocusManager#getAppTypeOwner(..) para extrair os nomes de pacote do proprietário atual de um determinado tipo de app que está em foco. Esse método pode retornar mais de um nome de pacote se o proprietário atual usar o recurso android:sharedUserId.

import android.car.CarAppFocusManager;

...

Car car = Car.createCar(this);
mAppFocusManager = (CarAppFocusManager)car.getCarManager(Car.APP_FOCUS_SERVICE);
List<String> focusOwnerPackageNames = mAppFocusManager.getAppTypeOwner(
              CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION);

if (focusOwnerPackageNames == null || focusOwnerPackageNames.isEmpty()) {
        // No Navigation app has focus
        // OEM may choose to show their default cluster view
} else {
       // focusOwnerPackageNames
       // Use the PackageManager to retrieve the cluster activity for the package(s)
       // returned in focusOwnerPackageNames
}

...

Apêndice: usar o app de exemplo

O AOSP fornece um app de exemplo que implementa a API Navigation State.

Para executar este app de exemplo:

  1. Crie e faça o flash do Android Auto em uma HU com suporte. Use as instruções de criação e flash do Android específicas para seu dispositivo. Para instruções, consulte Como usar placas de referência.
  2. Conecte uma tela secundária física ao HU (se houver suporte) ou ligue o HU secundário virtual:
    1. Selecione Modo de desenvolvedor no app Configurações.
    2. Acesse Configurações > Sistema > Avançado > Opções do desenvolvedor > Simular telas secundárias.
  3. Reinicializar o HU
  4. Para iniciar o app KitchenSink:
    1. Abra a gaveta.
    2. Acesse Cluster de inst..
    3. Clique em INICIAR METADADOS.

O KitchenSink solicita o foco de NAVEGAÇÃO, que instrui o serviço DirectRenderingCluster a mostrar uma interface de usuário simulada no cluster de instrumentos.