Instrument Cluster API

Mit der Instrument Cluster API (einer Android-API) können Sie Navigations-Apps wie Google Maps auf einem sekundären Display in einem Auto anzeigen, z. B. hinter dem Lenkrad auf dem Armaturenbrett. Auf dieser Seite wird beschrieben, wie Sie einen Dienst erstellen, um dieses sekundäre Display zu steuern und den Dienst in CarService einzubinden, damit Navigations-Apps eine Benutzeroberfläche anzeigen können.

Terminologie

Auf dieser Seite werden die folgenden Begriffe verwendet.

CarInstrumentClusterManager
Eine Instanz von CarManager, mit der externe Apps eine Aktivität auf dem Kombi-Instrument starten und Rückrufe empfangen können, wenn das Kombi-Instrument bereit ist, Aktivitäten anzuzeigen.
CarManager
Basisklasse aller Manager, die von externen Apps verwendet werden, um mit autospezifischen Diensten zu interagieren, die von CarService implementiert werden.
CarService
Android-Plattformdienst, der die Kommunikation zwischen externen Apps (einschließlich Google Maps) und fahrzeugspezifischen Funktionen wie dem Zugriff auf das Kombiinstrument ermöglicht.
Ziel
Das endgültige Ziel, zu dem das Fahrzeug navigiert.
Voraussichtliche Ankunftszeit
Die voraussichtliche Ankunftszeit an einem Zielort.
Infotainmentsystem (HU)
Primäre Recheneinheit in einem Auto. Die HU führt den gesamten Android-Code aus und ist mit dem zentralen Display im Auto verbunden.
Kombiinstrument
Sekundäres Display hinter dem Lenkrad und zwischen den Instrumenten. Dabei kann es sich um eine unabhängige Recheneinheit handeln, die über das interne Netzwerk des Autos (CAN-Bus) mit der HU verbunden ist, oder um ein sekundäres Display, das an die HU angeschlossen ist.
InstrumentClusterRenderingService
Basisklasse für den Dienst, der für die Kommunikation mit dem Kombiinstrument verwendet wird. OEMs müssen eine Erweiterung dieser Klasse bereitstellen, die mit der OEM-spezifischen Hardware interagiert.
KitchenSink-App
Test-App, die in Android Automotive enthalten ist.
Route
Ein bestimmter Pfad, den ein Fahrzeug nutzt, um ein Ziel zu erreichen.
Singleton-Dienst
Ein Android-Dienst mit dem Attribut android:singleUser. Zu einem bestimmten Zeitpunkt wird höchstens eine Instanz des Dienstes im Android-System ausgeführt.

Vorbereitung

Bevor Sie fortfahren, sollten Sie Folgendes parat haben:

  • Android-Entwicklungsumgebung Informationen zum Einrichten der Android-Entwicklungsumgebung finden Sie unter Build-Anforderungen.
  • Android-Quellcode herunterladen Laden Sie die aktuelle Version des Android-Quellcodes aus dem Branch „pi-car-release“ (oder höher) unter https://android.googlesource.com herunter.
  • Infotainmentsystem (HU) Ein Android-Gerät mit Android 9 oder höher. Dieses Gerät muss ein eigenes Display haben und in der Lage sein, das Display mit neuen Android-Builds zu flashen.
  • Instrument Cluster ist einer der folgenden Werte:
    • Physischer sekundärer Bildschirm, der an die Head-Unit angeschlossen ist: Die Gerätehardware und der Kernel müssen die Verwaltung mehrerer Bildschirme unterstützen.
    • Unabhängige Einheit: Jede Recheneinheit, die über eine Netzwerkverbindung mit der HU verbunden ist und einen Videostream auf ihrem eigenen Display empfangen und anzeigen kann.
    • Emuliertes Display: Während der Entwicklung können Sie eine der folgenden emulierten Umgebungen verwenden:
      • Simulierte sekundäre Displays: Wenn Sie ein simuliertes sekundäres Display in einer beliebigen AOSP-Android-Distribution aktivieren möchten, rufen Sie die Einstellungen Entwickleroptionen in der System-App Einstellungen auf und wählen Sie Sekundäre Displays simulieren aus.Diese Konfiguration entspricht dem Anschließen eines physischen sekundären Displays, mit der Einschränkung, dass dieses Display über dem primären Display eingeblendet wird.
      • Emuliertes Kombiinstrument: Der in AAOS enthaltene Android-Emulator bietet die Möglichkeit, ein Kombiinstrument mit ClusterRenderingService anzuzeigen.

Integrationsarchitektur

Integrationskomponenten

Jede Integration der Instrument Cluster API besteht aus diesen drei Komponenten:

  • CarService
  • Navigations-Apps
  • OEM-Kombiinstrument-Dienst

Integrationskomponenten

CarService

CarService fungiert als Vermittler zwischen Navigations-Apps und dem Auto. So wird dafür gesorgt, dass jeweils nur eine Navigations-App aktiv ist und nur Apps mit der Berechtigung android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL Daten an das Auto senden können.

CarService startet alle autospezifischen Dienste und bietet über eine Reihe von Managern Zugriff auf diese Dienste. Um mit den Diensten zu interagieren, können in Ihrem Auto ausgeführte Apps auf diese Manager zugreifen.

Für die Implementierung von Kombiinstrumenten müssen Automobil-OEMs eine benutzerdefinierte Implementierung von InstrumentClusterRendererService erstellen und ClusterRenderingService aktualisieren.

Beim Rendern eines Kombiinstruments liest CarService während des Bootvorgangs den InstrumentClusterRendererService-Schlüssel des ClusterRenderingService, um eine Implementierung von InstrumentClusterService zu finden. In AOSP verweist dieser Eintrag auf den Rendering-Dienst der Beispielclusterimplementierung der Navigation State API:

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

Der in diesem Eintrag erwähnte Dienst wird initialisiert und an CarService gebunden. Wenn Navigations-Apps wie Google Maps eine CarInstrumentClusterManager anfordern, stellt CarService einen Manager bereit, der den Kombi-Instrumenten-Status aus dem gebundenen InstrumentClusterRenderingService aktualisiert. In diesem Fall bezieht sich gebunden auf Android-Dienste.

Kombiinstrument-Dienst

OEMs müssen ein Android-Paket (APK) erstellen, das eine abgeleitete Klasse von ClusterRenderingService enthält.

Diese Klasse dient zwei Zwecken:

  • Bietet eine Schnittstelle zwischen Android und dem Gerät für die Darstellung des Kombiinstruments (das ist der Zweck dieser Seite).
  • Empfängt und rendert Updates zum Navigationsstatus, z. B. detaillierte Routenführung.

Für den ersten Zweck müssen OEM-Implementierungen von InstrumentClusterRendererService das sekundäre Display initialisieren, das zum Rendern von Informationen auf Bildschirmen in der Fahrerkabine verwendet wird, und diese Informationen an CarService übermitteln, indem die Methoden InstrumentClusterRendererService.setClusterActivityOptions() und InstrumentClusterRendererService.setClusterActivityState() aufgerufen werden.

Für die zweite Funktion muss der Instrument Cluster-Dienst eine Implementierung der ClusterRenderingService-Schnittstelle bereitstellen, die Navigationsstatus-Aktualisierungsereignisse (Ereignisse) empfängt, die als eventType und Ereignisdaten codiert sind, die in einem Bundle codiert sind.

Integrationsreihenfolge

Das folgende Diagramm veranschaulicht die Implementierung eines Navigationsstatus, der Updates rendert:

Integrationsreihenfolge

In dieser Abbildung stehen die Farben für Folgendes:

  • Gelb: CarService und CarNavigationStatusManager, die von der Android-Plattform bereitgestellt werden. Weitere Informationen finden Sie unter Auto und CAR_NAVIGATION_SERVICE.
  • Cyan InstrumentClusterRendererService vom OEM implementiert.
  • Lila: Die von Google und Drittentwicklern implementierte Navigations-App.
  • Grün CarAppFocusManager. Weitere Informationen finden Sie unten unter CarAppFocusManager API verwenden und unter CarAppFocusManager.

Der Informationsfluss des Navigationsstatus folgt dieser Reihenfolge:

  1. CarService initialisiert InstrumentClusterRenderingService.
  2. Während der Initialisierung aktualisiert InstrumentClusterRenderingService CarService mit:
    1. Eigenschaften der Instrumententafelanzeige, z. B. nicht verdeckte Grenzen (weitere Informationen dazu finden Sie unten).
    2. Aktivitätsoptionen, die zum Starten von Aktivitäten auf dem Kombi-Instrument erforderlich sind. Weitere Informationen finden Sie unter ActivityOptions.
  3. Eine Navigations-App wie Google Maps für Android Automotive oder eine beliebige Karten-App mit den erforderlichen Berechtigungen:
    1. Ruft ein CarAppFocusManager mithilfe der Car-Klasse aus car-lib ab.
    2. Bevor die detaillierte Routenführung beginnt, werden Aufrufe an CarAppFocusManager.requestFocus() ausgeführt, um CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION als appType-Parameter zu übergeben.
  4. CarAppFocusManager leitet diese Anfrage an CarService weiter. Wenn die Berechtigung erteilt wurde, prüft CarService das Navigations-App-Paket und sucht nach einer Aktivität, die mit der Kategorie android.car.cluster.NAVIGATION gekennzeichnet ist.
  5. Wenn die Navigations-App gefunden wird, verwendet sie die von InstrumentClusterRenderingService gemeldete ActivityOptions, um die Aktivität zu starten, und fügt die Eigenschaften der Instrumententafel als Extras in den Intent ein.

API einbinden

Die Implementierung von InstrumentClusterRenderingService muss folgende Anforderungen erfüllen:

  • Als Singleton-Dienst festgelegt werden, indem der folgende Wert zu AndroidManifest.xml hinzugefügt wird. Dies ist erforderlich, damit nur eine Kopie des Instrument Cluster-Dienstes ausgeführt wird, auch während der Initialisierung und beim Wechseln von Nutzern:
    android:singleUser="true"
  • Die Systemberechtigung BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE muss vorhanden sein. Dadurch wird sichergestellt, dass nur der Rendering-Dienst für das Kombiinstrument, der im Android-System-Image enthalten ist, jemals an CarService gebunden wird:
    <uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
    

InstrumentClusterRenderingService implementieren

So erstellen Sie den Dienst:

  1. Schreiben Sie eine Klasse, die von ClusterRenderingService abgeleitet wird, und fügen Sie dann einen entsprechenden Eintrag in die Datei AndroidManifest.xml ein. Diese Klasse steuert die Anzeige des Kombiinstruments und kann (optional) Daten der Navigation State API rendern.
  2. Verwenden Sie diesen Dienst während onCreate(), um die Kommunikation mit der Rendering-Hardware zu initialisieren. Zu den Optionen gehören:
  3. Bestimmen Sie das sekundäre Display, das für das Kombiinstrument verwendet werden soll.
  4. Erstellen Sie ein virtuelles Display, damit die Instrument Cluster App das gerenderte Bild rendert und an eine externe Einheit überträgt (mit einem Videostreaming-Format wie H.264).
  5. Wenn die oben angegebene Anzeige bereit ist, muss dieser Dienst InstrumentClusterRenderingService#setClusterActivityLaunchOptions() aufrufen, um die genaue ActivityOptions zu definieren, die zum Anzeigen einer Aktivität auf dem Kombiinstrument verwendet werden muss. Verwenden Sie diese Parameter:
    • category. ClusterRenderingService.
    • ActivityOptions. Eine ActivityOptions-Instanz, mit der eine Aktivität im Kombi-Instrument gestartet werden kann. Beispiel aus der Beispielimplementierung des Kombiinstruments in AOSP:
      getService().setClusterActivityLaunchOptions(
        CATEGORY_NAVIGATION,
        ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
  6. Wenn das Kombiinstrument bereit ist, Aktivitäten anzuzeigen, muss dieser Dienst InstrumentClusterRenderingService#setClusterActivityState() aufrufen. Verwenden Sie diese Parameter:
    • category ClusterRenderingService.
    • state-Bundle, das mit ClusterRenderingService generiert wurde. Geben Sie unbedingt die folgenden Daten an:
      • visible Gibt an, dass das Kombiinstrument sichtbar und bereit ist, Inhalte anzuzeigen.
      • unobscuredBounds Ein Rechteck, das den Bereich auf dem Kombiinstrument-Display definiert, in dem Inhalte sicher angezeigt werden können. Beispiel: Bereiche, die von Zifferblättern und Messinstrumenten abgedeckt werden.
  7. Überschreiben Sie die Service#dump()-Methode und melden Sie Statusinformationen, die für die Fehlerbehebung nützlich sind (weitere Informationen finden Sie unter dumpsys).

Beispielimplementierung von InstrumentClusterRenderingService

Im folgenden Beispiel wird eine InstrumentClusterRenderingService-Implementierung beschrieben, mit der ein VirtualDisplay erstellt wird, um die Inhalte des Instrument-Clusters auf einem physischen Remote-Display zu präsentieren.

Alternativ kann dieser Code die displayId eines physischen sekundären Displays übergeben, das mit der HU verbunden ist, sofern ein solches Display verfügbar ist.

/**
* 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);
  }
}

CarAppFocusManager API verwenden

Die CarAppFocusManager API bietet eine Methode namens getAppTypeOwner(), mit der der von OEMs geschriebene Clusterdienst jederzeit ermitteln kann, welche Navigations-App den Navigationsfokus hat. OEMs können die vorhandene CarAppFocusManager#addFocusListener()-Methode verwenden und dann mit getAppTypeOwner() herausfinden, welche App den Fokus hat. Mit diesen Informationen können OEMs:

  • Die im Kombiinstrument angezeigte Aktivität wird auf die Clusteraktivität umgestellt, die von der Navigations-App bereitgestellt wird, die den Fokus hat.
  • Kann erkennen, ob die fokussierte Navigations-App eine Clusteraktivität hat oder nicht. Wenn die fokussierte Navigations-App keine Clusteraktivität hat (oder wenn eine solche Aktivität deaktiviert ist), können OEMs dieses Signal an das DIM des Autos senden, damit die Navigationsfacette des Clusters vollständig übersprungen wird.

Mit CarAppFocusManager können Sie den aktuellen App-Fokus festlegen und darauf warten, z. B. aktive Navigation oder einen Sprachbefehl. Normalerweise wird nur eine Instanz einer solchen App im System aktiv ausgeführt (oder fokussiert).

Verwenden Sie die Methode CarAppFocusManager#addFocusListener(..), um auf Änderungen des App-Fokus zu reagieren:

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
}

Mit der Methode CarAppFocusManager#getAppTypeOwner(..) können Sie die Paketnamen des aktuellen Inhabers eines bestimmten App-Typs abrufen, der im Fokus steht. Bei dieser Methode kann mehr als ein Paketname zurückgegeben werden, wenn der aktuelle Inhaber die Funktion android:sharedUserId verwendet.

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
}

...

Vorlagen-Apps identifizieren

Bei vorlagenbasierten Navigations-Apps, die die Car App-Bibliothek verwenden, gibt CarAppFocusManager#getAppTypeOwner() den Paketnamen des Hosts zurück (z. B. com.google.android.apps.automotive.templates.host), da der Host den Systemfokus im Namen der Client-App hat.

Um die navigierende Client-App zu identifizieren, können OEMs den Paketnamen aus dem Navigationsstatus-Bundle extrahieren, das mit CarNavigationStatusManager gesendet wird. Der Paketname wird im von NavigationRenderer#onNavigationStateChanged(Bundle) empfangenen Bundle unter dem Schlüssel active_app_package_name gespeichert:

// 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)
    }
}

Anhang: Beispiel-App verwenden

AOSP bietet eine Beispiel-App, in der die Navigation State API implementiert ist.

So führen Sie diese Beispielanwendung aus:

  1. Erstelle und flashe Android Auto auf einem unterstützten Infotainmentsystem. Verwende die Android-Anleitung zum Erstellen und Flashen, die für dein Gerät gilt. Eine Anleitung findest du unter Referenzboards verwenden.
  2. Schließen Sie ein physisches sekundäres Display an die HU an (falls unterstützt) oder aktivieren Sie die virtuelle sekundäre HU:
    1. Wählen Sie in den Einstellungen Entwicklermodus aus.
    2. Gehen Sie zu Einstellungen > System > Erweitert > Entwickleroptionen > Sekundäre Displays simulieren.
  3. HU neu starten
  4. So starten Sie die KitchenSink App:
    1. Öffnen Sie die Schublade.
    2. Gehen Sie zu Inst. Cluster.
    3. Klicken Sie auf METADATEN STARTEN.

KitchenSink-Anfragen fordern den NAVIGATIONS-Fokus an, wodurch der DirectRenderingCluster-Dienst eine simulierte Benutzeroberfläche auf dem Kombiinstrument anzeigt.