Usa la API de clúster de instrumentos (una API de Android) para mostrar apps de navegación, incluido Google Maps, en una pantalla secundaria de un automóvil, como 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 integrarlo 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 de la rama pi-car-release (o una posterior) en https://android.googlesource.com.
- Unidad principal (HU). Un dispositivo Android capaz de ejecutar Android 9 (o versiones posteriores) Este dispositivo debe tener su propia pantalla y ser capaz de actualizarla con compilaciones nuevas de Android.
- Clúster de instrumentos es una de las siguientes opciones:
- 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 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 se superpone a la pantalla principal.
- Clúster de instrumentos emulado. El emulador de Android incluido con AAOS proporciona una opción para mostrar un clúster 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 del OEM
CarService
CarService
media entre las apps de navegación y el vehículo, 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 vehículo.
CarService
inicia todos los servicios específicos del automóvil y proporciona acceso a estos servicios a través de una serie de administradores. Para interactuar con los servicios, las apps que se ejecutan en el vehículo pueden acceder a estos administradores.
Para la implementación del panel de instrumentos, los OEM de la industria automotriz deben crear una implementación personalizada de InstrumentClusterRendererService y actualizar ClusterRenderingService.
Cuando se renderiza un clúster de instrumentos, durante el proceso de inicio, 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 clúster de muestra 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 los servicios de Android).
Servicio de clúster de instrumentos
Los OEMs 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 clúster 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 OEM de InstrumentClusterRendererService
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 de clúster de instrumentos debe proporcionar una implementación de la interfaz ClusterRenderingService que recibe eventos de actualización de 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
CarService
yCarNavigationStatusManager
proporcionados por la plataforma de Android Para obtener más información, consulta Vehículo y CAR_NAVIGATION_SERVICE. - Cian.
InstrumentClusterRendererService
que implementó el OEM. - Púrpura. La app de Navigation 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:
CarService
inicializa elInstrumentClusterRenderingService
.- Durante la inicialización,
InstrumentClusterRenderingService
actualizaCarService
con lo siguiente:- Propiedades de visualización del clúster de instrumentos, como los límites no ocultos (consulta más detalles sobre los límites no ocultos 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
CarAppFocusManager
con la clase Car de car-lib. - Antes de que comiencen las instrucciones sobre cómo llegar paso a paso, se realizan llamadas a
CarAppFocusManager.requestFocus()
para pasarCarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
como el parámetroappType
.
- Obtiene un
CarAppFocusManager
comunica esta solicitud aCarService
. Si se otorga,CarService
inspecciona 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
ActivityOptions
que informa elInstrumentClusterRenderingService
para iniciar la actividad y, además, incluye las propiedades de visualización del clúster de instrumentos como elementos adicionales en el intent.
Integra la API
La implementación de InstrumentClusterRenderingService
debe cumplir con los siguientes requisitos:
- Para designarlo como servicio singleton, agrega el siguiente valor a AndroidManifest.xml. Esto es necesario para garantizar que se ejecute una sola copia del servicio del clúster de instrumentos, incluso durante la inicialización y el cambio de usuario:
android:singleUser="true"
- Mantén el permiso del sistema
BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE
. Esto garantiza que solo el servicio de renderización del clúster de instrumentos incluido como parte de la imagen del sistema Android esté vinculado porCarService
:<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 renderizar datos de la API de Navigation State (de manera opcional). - Durante
onCreate()
, usa este servicio para inicializar la comunicación con el hardware de renderización. Estas son algunas de las opciones disponibles:- Determina la pantalla secundaria que se usará para el clúster de instrumentos.
- Crea una pantalla virtual para que la app del clúster de instrumentos 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 elActivityOptions
exacto que se debe usar para mostrar una actividad en el clúster de instrumentos. Usa estos parámetros:category.
ClusterRenderingService.ActivityOptions.
Una instancia deActivityOptions
que se puede usar para iniciar una actividad en el clúster de instrumentos. Por ejemplo, de la implementación de clúster de instrumentos de muestra en AOSP:getService().setClusterActivityLaunchOptions( CATEGORY_NAVIGATION, ActivityOptions.makeBasic() .setLaunchDisplayId(displayId));
- Cuando el clúster de instrumentos esté listo para mostrar actividades, este servicio debe invocar a
InstrumentClusterRenderingService#setClusterActivityState()
. Usa estos parámetros:category
ClusterRenderingService.- Paquete
state
generado con ClusterRenderingService. Asegúrate de proporcionar estos datos:visible
Especifica que el grupo de instrumentos es visible y está listo para mostrar contenido.unobscuredBounds
Un rectángulo que define el área dentro de la pantalla del panel de instrumentos en la que es seguro mostrar contenido. Por ejemplo, áreas cubiertas por diales y medidores.
- Anula el método
Service#dump()
y, luego, informa la información de estado útil para la depuración (consulta dumpsys para obtener más información).
Implementación de ejemplo de InstrumentClusterRenderingService
En el siguiente ejemplo, se describe una implementación de InstrumentClusterRenderingService
, que crea un VirtualDisplay
para presentar el contenido del clúster de instrumentos 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 OEMs 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 o no una actividad de clúster. Si la app de navegación enfocada no tiene una actividad de clúster (o si dicha actividad está inhabilitada), los OEMs pueden enviar esta señal 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 se ejecuta (o se enfoca) una instancia de una app de este tipo de forma activa en el sistema.
Usa el método CarAppFocusManager#addFocusListener(..)
para detectar cambios de 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 foco. Este método puede mostrar 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 } ...
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 en la memoria flash Android Auto en una HU compatible. Usa las instrucciones de compilación y actualización de Android específicas de tu dispositivo. Para obtener instrucciones, consulta Cómo usar paneles de referencia.
- Conecta una pantalla secundaria física a la HU (si es compatible) o activa 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 Clúster de inst..
- Haz clic en INICIAR METADATOS.
KitchenSink solicita el enfoque de NAVIGATION, que le indica al servicio DirectRenderingCluster
que muestre una interfaz de usuario simulada en el grupo de instrumentos.