API de clúster de instrumentos

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.

CarInstrumentClusterManager
Es una instancia de 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.
CarManager
Clase base de todos los administradores que usan las apps externas para interactuar con los servicios específicos del automóvil que implementa CarService.
CarService
Servicio de la plataforma de Android que proporciona comunicación entre apps externas (incluidos Google Maps) y funciones específicas del vehículo, como el acceso al clúster de instrumentos.
Destino
Es el destino final al que navegará el vehículo.
Hora estimada de llegada (ETA)
Es la hora estimada de llegada a un destino.
Consola central (HU)
Unidad de procesamiento principal incorporada en un automóvil. La HU ejecuta todo el código de Android y se conecta a la pantalla central del automóvil.
Clúster de instrumentos
Pantalla secundaria ubicada detrás del volante y entre los instrumentos del automóvil. Puede ser una unidad de procesamiento independiente conectada a la HU a través de la red interna del automóvil (bus CAN) o una pantalla secundaria conectada a la HU.
InstrumentClusterRenderingService
Clase base para el servicio que se usa para interactuar con la pantalla del clúster de instrumentos. Los OEMs deben proporcionar una extensión de esta clase que interactúe con el hardware específico del OEM.
App KitchenSink
App de prueba incluida en Android Automotive.
Ruta
Es una ruta específica por la que navega un vehículo para llegar a un destino.
Servicio singleton
Un servicio de Android con el atributo 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

Componentes de integración

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:

Secuencia de integración

En esta ilustración, los colores indican lo siguiente:

  • Amarillo CarService y CarNavigationStatusManager 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:

  1. CarService inicializa el InstrumentClusterRenderingService.
  2. Durante la inicialización, InstrumentClusterRenderingService actualiza CarService con lo siguiente:
    1. 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).
    2. 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.
  3. Una app de navegación (como Google Maps para Android Automotive o cualquier app de mapas con los permisos necesarios):
    1. Obtiene un CarAppFocusManager con la clase Car de car-lib.
    2. Antes de que comiencen las instrucciones sobre cómo llegar paso a paso, se realizan llamadas a CarAppFocusManager.requestFocus() para pasar CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION como el parámetro appType.
  4. CarAppFocusManager comunica esta solicitud a CarService. Si se otorga, CarService inspecciona el paquete de la app de navegación y localiza una actividad marcada con la categoría android.car.cluster.NAVIGATION.
  5. Si se encuentra, la app de navegación usa el ActivityOptions que informa el InstrumentClusterRenderingService 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 por CarService:
    <uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
    

Implementa InstrumentClusterRenderingService

Para compilar el servicio, haz lo siguiente:

  1. 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).
  2. 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).
  3. Cuando la pantalla indicada anteriormente esté lista, este servicio debe llamar a InstrumentClusterRenderingService#setClusterActivityLaunchOptions() para definir el ActivityOptions exacto que se debe usar para mostrar una actividad en el clúster de instrumentos. Usa estos parámetros:
    • category. ClusterRenderingService.
    • ActivityOptions. Una instancia de ActivityOptions 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));
  4. 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.
  5. 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:

  1. 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.
  2. Conecta una pantalla secundaria física a la HU (si es compatible) o activa la HU secundaria virtual:
    1. Selecciona Modo de desarrollador en la app de Configuración.
    2. Ve a Configuración > Sistema > Avanzado > Opciones para desarrolladores > Simular pantallas secundarias.
  3. Reinicia la HU
  4. Para iniciar la app de KitchenSink, haz lo siguiente:
    1. Abre el panel lateral.
    2. Ve a Clúster de inst..
    3. 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.