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 ele 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 oferece comunicação entre apps externos (incluindo o Google Maps) e recursos específicos do carro, como acesso ao painel 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 em um carro. A HU executa todo o código 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 (barramento CAN) ou uma tela secundária conectada à HU.
InstrumentClusterRenderingService
Classe de base para o serviço usado para interagir com a tela do painel de instrumentos. Os OEMs precisam fornecer uma extensão dessa classe que interaja com o hardware específico do OEM.
App KitchenSink
App de teste 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. A qualquer momento, no máximo uma instância do serviço é executada no sistema Android.

Pré-requisitos

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

  • Ambiente de desenvolvimento para Android. Para configurar o ambiente de desenvolvimento do Android, consulte Requisitos de build.
  • Faça o download do código-fonte do Android. Baixe a versão mais recente do código-fonte do Android na ramificação pi-car-release (ou mais recente) em https://android.googlesource.com.
  • Unidade principal (HU, na sigla em inglês). Um dispositivo Android capaz de executar o Android 9 (ou mais recente). Ele precisa ter uma tela própria e ser capaz de atualizar a tela com novas versões do Android.
  • Cluster de instrumentos é uma das seguintes opções:
    • Tela secundária física conectada à HU, se o hardware e o kernel do dispositivo forem compatíveis com o gerenciamento de várias telas.
    • Unidade independente: qualquer unidade computacional conectada à HU por uma conexão de rede, capaz de receber e mostrar um stream de vídeo no próprio display.
    • 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 AOSP Android, 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 a conectar uma tela secundária física, com a limitação de que ela é sobreposta à tela principal.
      • Painel de instrumentos emulado.O emulador do Android incluído no AAOS oferece uma opção para mostrar um painel 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 apps de navegação e o carro, garantindo que apenas um app de navegação esteja ativo por vez e que apenas apps com a permissão android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL possam enviar dados ao carro.

O CarService inicializa todos os serviços específicos do carro e fornece acesso a esses serviços por 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 painel de instrumentos, os OEMs automotivos precisam criar uma implementação personalizada do InstrumentClusterRendererService e atualizar o ClusterRenderingService.

Ao renderizar um painel 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, esta entrada aponta para o serviço de renderização da implementação de cluster de amostra 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, CarService fornece um gerenciador que atualiza o estado do painel de instrumentos do InstrumentClusterRenderingService vinculado. (Nesse caso, vinculado se refere aos 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 duas finalidades:

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

Para a primeira finalidade, as implementações de OEM de InstrumentClusterRendererService precisam inicializar a tela secundária usada para renderizar informações nas telas da cabine do carro e comunicar essas informações ao 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 do status de navegação, que são codificados como um eventType e dados de eventos 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 Car 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. O CarService inicializa o InstrumentClusterRenderingService.
  2. Durante a inicialização, o InstrumentClusterRenderingService atualiza CarService com:
    1. Propriedades de exibição do painel de instrumentos, como limites não obscurecidos (confira mais detalhes sobre limites não obscurecidos mais adiante).
    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. Recebe um CarAppFocusManager usando a classe Car da car-lib.
    2. Antes de iniciar a navegação guiada, chame CarAppFocusManager.requestFocus() para transmitir CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION como o parâmetro appType.
  4. O CarAppFocusManager comunica essa solicitação ao CarService. Se concedida, a 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 painel de instrumentos como extras na intent.

Integrar a API

A implementação de InstrumentClusterRenderingService precisa:

  • Seja 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"
  • Mantenha a permissão do sistema BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE. Isso garante que apenas o serviço de renderização do painel 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 InstrumentClusterRenderingService

Para criar o serviço:

  1. Escreva 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 Navigation State.
  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 painel de instrumentos.
    • Crie uma tela virtual para que o app Instrument Cluster 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 mostrar uma atividade no painel de instrumentos. Use estes parâmetros:
    • category. ClusterRenderingService.
    • ActivityOptions. Uma instância ActivityOptions que pode ser usada para iniciar uma atividade no cluster de instrumentos. Por exemplo, na implementação de cluster de instrumentos de amostra no AOSP:
      getService().setClusterActivityLaunchOptions(
        CATEGORY_NAVIGATION,
        ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
  4. Quando o painel de instrumentos estiver pronto para mostrar atividades, esse serviço vai invocar InstrumentClusterRenderingService#setClusterActivityState(). Use estes parâmetros:
    • category ClusterRenderingService.
    • Pacote state gerado com ClusterRenderingService. Informe estes dados:
      • visible Especifica o painel de instrumentos como visível e pronto para mostrar conteúdo.
      • unobscuredBounds Um retângulo que define a área dentro da tela do painel de instrumentos em que é seguro mostrar conteúdo. Por exemplo, áreas cobertas por mostradores e medidores.
  5. Substitua o método Service#dump() e informe o status útil 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 Instrument Cluster em uma tela física remota.

Como alternativa, esse código pode transmitir o displayId de uma tela secundária física conectada à HU, se houver uma 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 da navegação a qualquer momento. Os OEMs podem usar o método CarAppFocusManager#addFocusListener() atual e usar getAppTypeOwner() para saber qual app tem o foco. Com essas informações, os OEMs podem:

  • Mude a atividade mostrada no cluster para a atividade fornecida pelo app de navegação em foco.
  • Pode detectar se o app de navegação em foco tem uma atividade de cluster ou não. Se o app de navegação em foco 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 ignorada completamente.

Use CarAppFocusManager para definir e detectar o foco atual do app, como navegação ativa ou um comando de voz. Normalmente, apenas uma instância desse app está em execução (ou em foco) 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 recuperar os nomes de pacotes do proprietário atual de um determinado tipo de app 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
}

...

Identificar apps de modelo

Para apps de navegação baseados em modelos que usam a biblioteca Car App, CarAppFocusManager#getAppTypeOwner() retorna o nome do pacote do host (por exemplo, com.google.android.apps.automotive.templates.host) porque o host mantém o foco do sistema em nome do app cliente.

Para identificar o app cliente de navegação, os OEMs podem extrair o nome do pacote do pacote de estado de navegação enviado com CarNavigationStatusManager. O nome do pacote é armazenado na chave active_app_package_name no pacote recebido por NavigationRenderer#onNavigationStateChanged(Bundle):

// In your NavigationRenderer implementation
@Override
public void onNavigationStateChanged(Bundle bundle) {
    if (bundle.containsKey("active_app_package_name")) {
        String activeAppPackage = bundle.getString("active_app_package_name");
        // Use the package name to identify the navigating app (e.g., com.waze)
    }
}

Apêndice: usar o app de exemplo

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

Para executar este app de exemplo:

  1. Crie e instale o Android Auto em uma HU compatível. Use as instruções de criação e atualização com flash do Android específicas para seu dispositivo. Para instruções, consulte Como usar quadros de referência.
  2. Conecte uma tela secundária física à HU (se compatível) ou ative a HU secundária 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. Reiniciar a HU
  4. Para iniciar o app KitchenSink:
    1. Abra a gaveta.
    2. Acesse Cluster de instâncias.
    3. Clique em INICIAR METADADOS.

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