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.
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.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
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:
Nesta ilustração, as cores indicam o seguinte:
- Amarelo.
CarService
eCarNavigationStatusManager
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:
CarService
inicializa oInstrumentClusterRenderingService
.- Durante a inicialização, o
InstrumentClusterRenderingService
atualizaCarService
com:- Propriedades de exibição do cluster de instrumentos, como limites não ocultos (mais detalhes sobre limites não ocultos mais tarde).
- Opções de atividade necessárias para iniciar atividades na tela do cluster de instrumentos. Para saber mais, consulte ActivityOptions.
- Um app de navegação (como o Google Maps para Android Automotive ou qualquer app de mapas
com as permissões necessárias):
- Obtém um
CarAppFocusManager
usando a classe Car da car-lib. - Antes de iniciar as direções passo a passo, as chamadas para
CarAppFocusManager.requestFocus()
transmitemCarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
como o parâmetroappType
.
- Obtém um
- O
CarAppFocusManager
comunica essa solicitação aoCarService
. Se concedido,CarService
inspeciona o pacote do app de navegação e localiza uma atividade marcada com a categoriaandroid.car.cluster.NAVIGATION
. - Se encontrado, o app de navegação usa o
ActivityOptions
informado peloInstrumentClusterRenderingService
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 peloCarService
:<uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
Implementar o InstrumentClusterRenderingService
Para criar o serviço:
- 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. - 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).
- Quando a tela indicada acima estiver pronta, esse serviço precisará chamar
InstrumentClusterRenderingService#setClusterActivityLaunchOptions()
para definir oActivityOptions
exato que precisa ser usado para exibir uma atividade no cluster de instrumentos. Use estes parâmetros:category.
ClusterRenderingService.ActivityOptions.
Uma instância deActivityOptions
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));
- 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.
- 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:
- 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.
- Conecte uma tela secundária física ao HU (se houver suporte) ou ligue o HU
secundário virtual:
- Selecione Modo de desenvolvedor no app Configurações.
- Acesse Configurações > Sistema > Avançado > Opções do desenvolvedor > Simular telas secundárias.
- Reinicializar o HU
- Para iniciar o app KitchenSink:
- Abra a gaveta.
- Acesse Cluster de inst..
- 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.