Usa la API de Instrument Cluster (una API de Android) para mostrar apps de navegación, como Google Maps, en una pantalla secundaria del automóvil, por ejemplo, detrás del volante en el panel de instrumentos. En esta página, se describe cómo crear un servicio para controlar esa pantalla secundaria y cómo integrar el servicio con CarService para que las apps de navegación puedan mostrar una interfaz de usuario.
Terminología
En esta página, se usan los siguientes términos.
CarManager que permite que las apps externas inicien una actividad en el clúster de instrumentos y reciban devoluciones de llamada cuando el clúster de instrumentos esté listo para mostrar actividades.android:singleUser. En cualquier momento, se ejecuta como máximo una instancia del servicio en el sistema Android.Requisitos previos
Antes de continuar, asegúrate de tener los siguientes elementos:
- Entorno de desarrollo de Android. Para configurar el entorno de desarrollo de Android, consulta Requisitos de compilación.
- Descarga el código fuente de Android. Obtén la versión más reciente del código fuente de Android desde la rama pi-car-release (o posterior) en https://android.googlesource.com.
- Consola central (HU). Un dispositivo Android capaz de ejecutar Android 9 (o versiones posteriores) Este dispositivo debe tener su propia pantalla y poder actualizarla con compilaciones nuevas de Android.
- Clúster de instrumentos es uno de los siguientes:
- Pantalla secundaria física conectada a la HU. Si el hardware y el kernel del dispositivo admiten la administración de varias pantallas.
- Unidad independiente. Cualquier unidad de procesamiento conectada a la HU a través de una conexión de red, capaz de recibir y mostrar una transmisión de video en su propia pantalla.
- Pantalla emulada. Durante el desarrollo, puedes usar uno de estos entornos emulados:
- Pantallas secundarias simuladas. Para habilitar una pantalla secundaria simulada en cualquier distribución de Android del AOSP, ve a la configuración de Opciones para desarrolladores en la app del sistema Configuración y, luego, selecciona Simular pantallas secundarias. Esta configuración equivale a conectar una pantalla secundaria física, con la limitación de que esta pantalla se superpone a la pantalla principal.
- Clúster de instrumentos emulado. El emulador de Android incluido en AAOS proporciona una opción para mostrar un panel de instrumentos con ClusterRenderingService.
Arquitectura de la integración
Componentes de integración
Cualquier integración de la API de Instrument Cluster consta de estos tres componentes:
CarService- Apps de navegación
- Servicio de clúster de instrumentos OEM

CarService
CarService actúa como intermediario entre las apps de navegación y el automóvil, lo que garantiza que solo una app de navegación esté activa en un momento determinado y que solo las apps con el permiso android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL puedan enviar datos al automóvil.
CarService inicializa todos los servicios específicos del automóvil y proporciona acceso a ellos a través de una serie de administradores. Para interactuar con los servicios, las apps que se ejecutan en el auto pueden acceder a estos administradores.
Para la implementación del panel de instrumentos, los OEM automotrices deben crear una implementación personalizada de InstrumentClusterRendererService y actualizar ClusterRenderingService.
Cuando se renderiza un panel de instrumentos, durante el proceso de arranque, CarService lee la clave InstrumentClusterRendererService de ClusterRenderingService para ubicar una implementación de InstrumentClusterService. En AOSP, esta entrada apunta al servicio de renderización de la implementación de muestra del clúster de la API de Navigation State:
<string name="instrumentClusterRendererService"> android.car.cluster/.ClusterRenderingService </string>
El servicio al que se hace referencia en esta entrada se inicializa y se vincula a CarService. Cuando las apps de navegación, como Google Maps, solicitan un CarInstrumentClusterManager, CarService proporciona un administrador que actualiza el estado del clúster de instrumentos desde el InstrumentClusterRenderingService vinculado.
(En este caso, vinculado se refiere a Servicios de Android).
Servicio de clúster de instrumentos
Los OEM deben crear un paquete de Android (APK) que contenga una subclase de ClusterRenderingService.
Esta clase tiene dos propósitos:
- Proporciona una interfaz de Android y el dispositivo de renderización del panel de instrumentos (el propósito de esta página).
- Recibe y renderiza actualizaciones del estado de navegación, como instrucciones de navegación paso a paso.
Para el primer propósito, las implementaciones de InstrumentClusterRendererService del OEM deben inicializar la pantalla secundaria que se usa para renderizar información en las pantallas de la cabina del automóvil y comunicar esta información a CarService llamando a los métodos InstrumentClusterRendererService.setClusterActivityOptions() y InstrumentClusterRendererService.setClusterActivityState().
Para la segunda función, el servicio Instrument Cluster debe proporcionar una implementación de la interfaz ClusterRenderingService que recibe eventos de actualización del estado de navegación, que se codifican como un eventType y datos de eventos codificados en un paquete.
Secuencia de integración
En el siguiente diagrama, se ilustra la implementación de un estado de navegación que renderiza actualizaciones:
En esta ilustración, los colores indican lo siguiente:
- Amarillo.
CarServiceyCarNavigationStatusManagerproporcionados por la plataforma de Android. Para obtener más información, consulta Car y CAR_NAVIGATION_SERVICE. - Cian.
InstrumentClusterRendererServiceimplementado por el OEM. - Púrpura. Es la app de navegación implementada por Google y desarrolladores externos.
- Verde.
CarAppFocusManager. Para obtener más información, consulta Cómo usar la API de CarAppFocusManager a continuación y CarAppFocusManager.
El flujo de información del estado de navegación sigue esta secuencia:
CarServiceinicializa elInstrumentClusterRenderingService.- Durante la inicialización,
InstrumentClusterRenderingServiceactualizaCarServicecon lo siguiente:- Propiedades de la pantalla del panel de instrumentos, como los límites de visibilidad (consulta más detalles sobre los límites de visibilidad más adelante)
- Son las opciones de actividad necesarias para iniciar actividades dentro de la pantalla del clúster de instrumentos. Para obtener más información, consulta ActivityOptions.
- Una app de navegación (como Google Maps para Android Automotive o cualquier app de mapas con los permisos necesarios):
- Obtiene un
CarAppFocusManagercon la clase Car de car-lib. - Antes de que comiencen las instrucciones paso a paso, se llama a
CarAppFocusManager.requestFocus()para pasarCarAppFocusManager.APP_FOCUS_TYPE_NAVIGATIONcomo el parámetroappType.
- Obtiene un
CarAppFocusManagercomunica esta solicitud aCarService. Si se otorga,CarServiceinspecciona el paquete de la app de navegación y localiza una actividad marcada con la categoríaandroid.car.cluster.NAVIGATION.- Si se encuentra, la app de navegación usa el
ActivityOptionsinformado por elInstrumentClusterRenderingServicepara iniciar la actividad y, además, incluye las propiedades de visualización del panel de instrumentos como extras en el intent.
Integra la API
La implementación de InstrumentClusterRenderingService debe hacer lo siguiente:
- Se debe designar como un servicio singleton agregando el siguiente valor a AndroidManifest.xml. Esto es necesario para garantizar que se ejecute una sola copia del servicio Instrument Cluster, incluso durante la inicialización y el cambio de usuario:
android:singleUser="true" - Tener el permiso del sistema
BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICEEsto garantiza que solo el servicio de renderización del panel de instrumentos incluido como parte de la imagen del sistema Android se vincule conCarService:<uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
Implementa InstrumentClusterRenderingService
Para compilar el servicio, haz lo siguiente:
- Escribe una clase que se extienda desde ClusterRenderingService y, luego, agrega una entrada correspondiente a tu archivo
AndroidManifest.xml. Esta clase controla la pantalla del clúster de instrumentos y puede (opcionalmente) renderizar datos de la API de Navigation State. - Durante
onCreate(), usa este servicio para inicializar la comunicación con el hardware de renderización. Estas son algunas opciones:- Determina la pantalla secundaria que se usará para el panel de instrumentos.
- Crea una pantalla virtual para que la app de Instrument Cluster renderice y transmita la imagen renderizada a una unidad externa (con un formato de transmisión de video, como H.264).
- Cuando la pantalla indicada anteriormente esté lista, este servicio debe llamar a
InstrumentClusterRenderingService#setClusterActivityLaunchOptions()para definir elActivityOptionsexacto que se debe usar para mostrar una actividad en el panel de instrumentos. Usa estos parámetros:category.ClusterRenderingService.ActivityOptions.Instancia deActivityOptionsque se puede usar para iniciar una actividad en el clúster de instrumentos. Por ejemplo, en la implementación de Instrument Cluster de muestra en AOSP:getService().setClusterActivityLaunchOptions( CATEGORY_NAVIGATION, ActivityOptions.makeBasic() .setLaunchDisplayId(displayId));
- Cuando el panel de instrumentos esté listo para mostrar actividades, este servicio debe invocar
InstrumentClusterRenderingService#setClusterActivityState(). Usa estos parámetros:categoryClusterRenderingService.- Paquete
stategenerado con ClusterRenderingService. Asegúrate de proporcionar los siguientes datos:visibleEspecifica que el panel de instrumentos está visible y listo para mostrar contenido.unobscuredBoundsUn rectángulo que define el área dentro de la pantalla del panel de instrumentos en la que es seguro mostrar contenido. Por ejemplo, las áreas cubiertas por los diales y los indicadores.
- Anula el método
Service#dump()y registra información de estado útil para la depuración (consulta dumpsys para obtener más información).
Ejemplo de implementación de InstrumentClusterRenderingService
En el siguiente ejemplo, se describe una implementación de InstrumentClusterRenderingService, que crea un VirtualDisplay para presentar el contenido de Instrument Cluster en una pantalla física remota.
Como alternativa, este código podría pasar el displayId de una pantalla secundaria física conectada a la HU, si se sabe que hay una disponible.
/** * 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); } }
Usa la API de CarAppFocusManager
La API de CarAppFocusManager proporciona un método llamado getAppTypeOwner(), que permite que el servicio de clúster escrito por los OEM sepa qué app de navegación tiene el enfoque de navegación en un momento determinado. Los OEMs pueden usar el método CarAppFocusManager#addFocusListener() existente y, luego, usar getAppTypeOwner() para saber qué app tiene el enfoque. Con esta información, los OEM pueden hacer lo siguiente:
- Cambia la actividad que se muestra en el clúster a la actividad del clúster que proporciona la app de navegación que tiene el enfoque.
- Puede detectar si la app de navegación enfocada tiene una actividad de clúster o no. Si la app de navegación enfocada no tiene una actividad de clúster (o si esa actividad está inhabilitada), los OEM pueden enviar este indicador al DIM del automóvil para que se omita por completo la faceta de navegación del clúster.
Usa CarAppFocusManager para establecer y escuchar el enfoque actual de la app, como la navegación activa o un comando por voz. Por lo general, solo una instancia de este tipo de app se ejecuta (o se enfoca) de forma activa en el sistema.
Usa el método CarAppFocusManager#addFocusListener(..) para detectar cambios en el enfoque de la 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 }
Usa el método CarAppFocusManager#getAppTypeOwner(..) para recuperar los nombres de los paquetes del propietario actual de un tipo de app determinado que está en primer plano. Este método puede devolver más de un nombre de paquete si el propietario actual usa la función 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 } ...
Identifica apps de plantillas
En el caso de las apps de navegación basadas en plantillas que usan la biblioteca de Car App, CarAppFocusManager#getAppTypeOwner() devuelve el nombre del paquete del host (por ejemplo, com.google.android.apps.automotive.templates.host) porque el host mantiene el enfoque del sistema en nombre de la app cliente.
Para identificar la app cliente de navegación, los OEM pueden extraer el nombre del paquete del paquete de estado de navegación que se envía con CarNavigationStatusManager. El nombre del paquete se almacena con la clave active_app_package_name en el paquete recibido 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: Usa la app de ejemplo
El AOSP proporciona una app de ejemplo que implementa la API de Navigation State.
Para ejecutar esta app de ejemplo, haz lo siguiente:
- Compila y escribe Android Auto en una HU compatible. Usa las instrucciones de compilación y escritura en la memoria flash de Android específicas para tu dispositivo. Para obtener instrucciones, consulta Cómo usar los paneles de referencia.
- Conecta una pantalla secundaria física a la HU (si es compatible) o enciende la HU secundaria virtual:
- Selecciona Modo de desarrollador en la app de Configuración.
- Ve a Configuración > Sistema > Avanzado > Opciones para desarrolladores > Simular pantallas secundarias.
- Reinicia la HU.
- Para iniciar la app de KitchenSink, haz lo siguiente:
- Abre el panel lateral.
- Ve a Inst. Cluster.
- Haz clic en INICIAR METADATOS.
KitchenSink solicita el enfoque de NAVIGATION, lo que indica al servicio DirectRenderingCluster que muestre una interfaz de usuario simulada en el panel de instrumentos.