API Instrument Cluster

Utilisez l'API Instrument Cluster (une API Android) pour afficher des applications de navigation, y compris Google Maps, sur un écran secondaire dans une voiture, par exemple derrière le volant sur le tableau de bord. Cette page explique comment créer un service pour contrôler cet écran secondaire et l'intégrer à CarService afin que les applications de navigation puissent afficher une interface utilisateur.

Terminologie

Les termes suivants sont utilisés sur cette page.

CarInstrumentClusterManager
Instance de CarManager qui permet aux applications externes de lancer une activité sur le groupe d'instruments et de recevoir des rappels lorsque le groupe d'instruments est prêt à afficher des activités.
CarManager
Classe de base de tous les gestionnaires utilisés par les applications externes pour interagir avec les services spécifiques aux voitures implémentés par CarService.
CarService
Service de plate-forme Android qui assure la communication entre les applications externes (y compris Google Maps) et les fonctionnalités spécifiques à la voiture, telles que l'accès au tableau de bord.
Destination
 Destination finale vers laquelle le véhicule se dirigera.
Heure d'arrivée estimée
Heure d'arrivée prévue à une destination.
Unité principale (UP)
Unité de calcul principale intégrée à une voiture. L'UP exécute tout le code Android et est connectée à l'écran central de la voiture.
Cluster d'instruments
Écran secondaire situé derrière le volant et entre les instruments de la voiture. Il peut s'agir d'une unité de calcul indépendante connectée à l'UP via le réseau interne de la voiture (bus CAN) ou d'un écran secondaire rattaché à l'UP.
InstrumentClusterRenderingService
Classe de base pour le service utilisé pour l'interface avec l'écran du tableau de bord. Les OEM doivent fournir une extension de cette classe qui interagit avec le matériel spécifique à l'OEM.
Application KitchenSink
Application de test incluse avec Android Automotive.
Itinéraire
 Chemin spécifique emprunté par un véhicule pour arriver à destination.
Service singleton
Service Android avec l'attribut android:singleUser. À tout moment, au maximum une instance du service s'exécute sur le système Android.

Prérequis

Avant de continuer, assurez-vous de disposer des éléments suivants :

  • Environnement de développement Android : pour configurer l'environnement de développement Android, consultez Exigences de compilation.
  • Téléchargez le code source Android. Obtenez la dernière version du code source Android à partir de la branche pi-car-release (ou version ultérieure) sur https://android.googlesource.com.
  • Unité principale (UP) : un appareil Android capable d'exécuter Android 9 (ou version ultérieure). Cet appareil doit disposer de son propre écran et être capable d'afficher les nouvelles versions d'Android.
  • Cluster d'instruments est l'une des valeurs suivantes :
    • Écran secondaire physique connecté à l'unité principale : si le matériel et le noyau de l'appareil permettent de gérer plusieurs écrans.
    • Unité indépendante : Toute unité de calcul connectée à l'UP via une connexion réseau, capable de recevoir et d'afficher un flux vidéo sur son propre écran.
    • Écran émulé : pendant le développement, vous pouvez utiliser l'un des environnements émulés suivants :
      • Écrans secondaires simulés : pour activer un écran secondaire simulé sur n'importe quelle distribution AOSP Android, accédez aux paramètres Options pour les développeurs dans l'application système Paramètres, puis sélectionnez Simuler des écrans secondaires.Cette configuration équivaut à connecter un écran secondaire physique, avec la limite que cet écran est superposé à l'écran principal.
      • Tableau de bord émulé : L'émulateur Android inclus dans AAOS permet d'afficher un tableau de bord avec ClusterRenderingService.

Architecture d'intégration

Composants d'intégration

Toute intégration de l'API Instrument Cluster se compose de ces trois éléments :

  • CarService
  • Applications de navigation
  • Service de cluster d'instruments OEM

Composants d'intégration

CarService

CarService sert d'intermédiaire entre les applications de navigation et la voiture. Il s'assure qu'une seule application de navigation est active à un moment donné et que seules les applications disposant de l'autorisation android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL peuvent envoyer des données à la voiture.

CarService amorce tous les services spécifiques à la voiture et fournit un accès à ces services par le biais d'une série de gestionnaires. Pour interagir avec les services, les applications exécutées dans la voiture peuvent accéder à ces gestionnaires.

Pour implémenter le bloc d'instruments, les OEM automobiles doivent créer une implémentation personnalisée d'InstrumentClusterRendererService et mettre à jour ClusterRenderingService.

Lors du rendu d'un tableau de bord, pendant le processus de démarrage, CarService lit la clé InstrumentClusterRendererService de ClusterRenderingService pour localiser une implémentation de InstrumentClusterService. Dans AOSP, cette entrée pointe vers le service de rendu de l'implémentation de cluster de l'exemple d'API Navigation State :

<string name="instrumentClusterRendererService">
android.car.cluster/.ClusterRenderingService
</string>

Le service mentionné dans cette entrée est initialisé et lié à CarService. Lorsque des applications de navigation, comme Google Maps, demandent un CarInstrumentClusterManager, CarService fournit un gestionnaire qui met à jour l'état du groupe d'instruments à partir du InstrumentClusterRenderingService lié. (Dans ce cas, lié fait référence à Services Android.)

Service de cluster d'instruments

Les OEM doivent créer un package Android (APK) contenant une sous-classe de ClusterRenderingService.

Cette classe a deux objectifs :

  • Fournit une interface entre Android et le périphérique de rendu du tableau de bord (l'objectif de cette page).
  • Reçoit et affiche les mises à jour de l'état de navigation, comme les instructions de navigation détaillées.

Pour le premier objectif, les implémentations OEM de InstrumentClusterRendererService doivent initialiser l'écran secondaire utilisé pour afficher des informations sur les écrans de l'habitacle et communiquer ces informations à CarService en appelant les méthodes InstrumentClusterRendererService.setClusterActivityOptions() et InstrumentClusterRendererService.setClusterActivityState().

Pour la deuxième fonction, le service Instrument Cluster doit fournir une implémentation de l'interface ClusterRenderingService qui reçoit les événements de mise à jour de l'état de navigation, qui sont encodés en tant que eventType et les données d'événement encodées dans un bundle.

Séquence d'intégration

Le schéma suivant illustre l'implémentation d'un état de navigation qui affiche les mises à jour :

Séquence d&#39;intégration

Dans cette illustration, les couleurs indiquent les éléments suivants :

  • Jaune : CarService et CarNavigationStatusManager fournis par la plate-forme Android. Pour en savoir plus, consultez Voiture et CAR_NAVIGATION_SERVICE.
  • Cyan InstrumentClusterRendererService implémenté par l'OEM.
  • Violet : application de navigation implémentée par Google et des développeurs tiers.
  • Green. CarAppFocusManager. Pour en savoir plus, consultez Utiliser l'API CarAppFocusManager ci-dessous et CarAppFocusManager.

Le flux d'informations sur l'état de navigation suit cette séquence :

  1. CarService initialise InstrumentClusterRenderingService.
  2. Lors de l'initialisation, InstrumentClusterRenderingService met à jour CarService avec :
    1. Propriétés d'affichage du tableau de bord, telles que les limites non masquées (plus de détails sur les limites non masquées ci-dessous).
    2. Options d'activité nécessaires pour lancer des activités sur l'écran du groupe d'instruments. Pour en savoir plus, consultez ActivityOptions.
  3. Une application de navigation (telle que Google Maps pour Android Automotive ou toute application de cartographie disposant des autorisations requises) :
    1. Obtient un CarAppFocusManager à l'aide de la classe Car de car-lib.
    2. Avant le début de la navigation détaillée, appelez CarAppFocusManager.requestFocus() pour transmettre CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION en tant que paramètre appType.
  4. CarAppFocusManager communique cette requête à CarService. Si elle est accordée, CarService inspecte le package d'application de navigation et localise une activité marquée avec la catégorie android.car.cluster.NAVIGATION.
  5. Si elle est trouvée, l'application de navigation utilise la ActivityOptions signalée par le InstrumentClusterRenderingService pour lancer l'activité et inclut les propriétés d'affichage du tableau de bord en tant qu'extras dans l'intent.

Intégrer l'API

L'implémentation de InstrumentClusterRenderingService doit :

  • être désigné comme service singleton en ajoutant la valeur suivante à AndroidManifest.xml. Cela est nécessaire pour s'assurer qu'une seule copie du service Instrument Cluster s'exécute, même pendant l'initialisation et le changement d'utilisateur :
    android:singleUser="true"
  • Détenir l'autorisation système BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE. Cela garantit que seul le service de rendu du tableau de bord inclus dans l'image système Android est lié par le CarService :
    <uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
    

Implémenter InstrumentClusterRenderingService

Pour créer le service :

  1. Écrivez une classe qui s'étend à partir de ClusterRenderingService, puis ajoutez une entrée correspondante à votre fichier AndroidManifest.xml. Cette classe contrôle l'affichage du groupe d'instruments et peut (facultativement) afficher les données de l'API Navigation State.
  2. Pendant onCreate(), utilisez ce service pour initialiser la communication avec le matériel de rendu. Les options sont les suivantes :
    • Déterminez l'écran secondaire à utiliser pour le tableau de bord.
    • Créez un écran virtuel pour que l'application Instrument Cluster affiche et transmette l'image rendue à une unité externe (à l'aide d'un format de streaming vidéo, tel que H.264).
  3. Lorsque l'affichage indiqué ci-dessus est prêt, ce service doit appeler InstrumentClusterRenderingService#setClusterActivityLaunchOptions() pour définir le ActivityOptions exact qui doit être utilisé pour afficher une activité sur le tableau de bord. Utilisez ces paramètres :
    • category. ClusterRenderingService.
    • ActivityOptions. Instance ActivityOptions pouvant être utilisée pour lancer une activité dans le cluster d'instruments. Par exemple, à partir de l'implémentation du cluster d'instruments dans AOSP :
      getService().setClusterActivityLaunchOptions(
        CATEGORY_NAVIGATION,
        ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
  4. Lorsque le tableau de bord est prêt à afficher les activités, ce service doit appeler InstrumentClusterRenderingService#setClusterActivityState(). Utilisez ces paramètres :
    • category ClusterRenderingService.
    • Bundle state généré avec ClusterRenderingService. Veillez à fournir les données suivantes :
      • visible Spécifie que le tableau de bord est visible et prêt à afficher du contenu.
      • unobscuredBounds Rectangle qui définit la zone de l'écran du tableau de bord dans laquelle il est possible d'afficher du contenu. Par exemple, les zones couvertes par les cadrans et les jauges.
  5. Remplacez la méthode Service#dump() et signalez les informations d'état utiles pour le débogage (pour en savoir plus, consultez dumpsys).

Exemple d'implémentation de InstrumentClusterRenderingService

L'exemple suivant décrit une implémentation InstrumentClusterRenderingService, qui crée un VirtualDisplay pour présenter le contenu du cluster d'instruments sur un écran physique distant.

Ce code peut également transmettre le displayId d'un écran secondaire physique connecté à l'UP, s'il est connu pour être 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);
  }
}

Utiliser l'API CarAppFocusManager

L'API CarAppFocusManager fournit une méthode nommée getAppTypeOwner(), qui permet au service de cluster écrit par les OEM de savoir quelle application de navigation a le focus de navigation à un moment donné. Les OEM peuvent utiliser la méthode CarAppFocusManager#addFocusListener() existante, puis getAppTypeOwner() pour savoir quelle application est au premier plan. Grâce à ces informations, les OEM peuvent :

  • Basculez l'activité affichée dans le cluster vers l'activité du cluster fournie par l'application de navigation sélectionnée.
  • Peut détecter si l'application de navigation sélectionnée possède une activité de cluster ou non. Si l'application de navigation sélectionnée ne comporte aucune activité de cluster (ou si cette activité est désactivée), les OEM peuvent envoyer ce signal au DIM de la voiture afin que la facette de navigation du cluster soit complètement ignorée.

Utilisez CarAppFocusManager pour définir et écouter le focus de l'application actuelle, comme la navigation active ou une commande vocale. En général, une seule instance de ce type d'application est en cours d'exécution (ou est sélectionnée) dans le système.

Utilisez la méthode CarAppFocusManager#addFocusListener(..) pour écouter les changements de focus de l'application :

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
}

Utilisez la méthode CarAppFocusManager#getAppTypeOwner(..) pour récupérer les noms de package du propriétaire actuel d'un type d'application donné qui est sélectionné. Cette méthode peut renvoyer plusieurs noms de package si le propriétaire actuel utilise la fonctionnalité 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
}

...

Identifier les applications de modèle

Pour les applications de navigation basées sur des modèles qui utilisent la bibliothèque d'applications automobiles, CarAppFocusManager#getAppTypeOwner() renvoie le nom du package de l'hôte (par exemple, com.google.android.apps.automotive.templates.host), car l'hôte détient le focus système au nom de l'application cliente.

Pour identifier l'application cliente de navigation, les OEM peuvent extraire le nom du package du bundle d'état de navigation envoyé avec CarNavigationStatusManager. Le nom du package est stocké sous la clé active_app_package_name dans le bundle reçu par 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)
    }
}

Annexe : Utiliser l'application exemple

AOSP fournit une application exemple qui implémente l'API Navigation State.

Pour exécuter cet exemple d'application :

  1. Créez et installez Android Auto sur une unité principale compatible. Suivez les instructions de compilation et de flashage d'Android spécifiques à votre appareil. Pour obtenir des instructions, consultez Utiliser des tableaux de référence.
  2. Connectez un écran secondaire physique à l'unité principale (si elle est compatible) ou activez l'unité principale secondaire virtuelle :
    1. Sélectionnez Mode développeur dans l'application Paramètres.
    2. Accédez à Paramètres > Système > Avancé > Options pour les développeurs > Simuler les écrans secondaires.
  3. Redémarrer l'UP
  4. Pour lancer l'application KitchenSink :
    1. Ouvrez le tiroir.
    2. Accédez à Cluster d'instances.
    3. Cliquez sur DÉMARRER LES MÉTADONNÉES.

KitchenSink demande à ce que la NAVIGATION soit mise au point, ce qui indique au service DirectRenderingCluster d'afficher une interface utilisateur simulée sur le tableau de bord.