Google is committed to advancing racial equity for Black communities. See how.

Instrument Cluster

Use the Instrument Cluster API (an Android API) to display navigation apps, including Google Maps, on a secondary display in a car, such as behind the steering wheel on the instrument panel. This article describes how to create a service to control that secondary display and to then integrate the service with CarService so that navigation apps can display a user interface.

Terminology

The following terms are used throughout this article:

Term Description
CarInstrumentClusterManager A CarManager that enables external apps to launch an activity on the Instrument Cluster and receive callbacks when the Instrument Cluster is ready to display activities.
CarManager Base class of all managers used by external apps to interact with car-specific services implemented by CarService.
CarService Android Platform service that provides communication between external apps (including Google Maps) and car-specific features, such as Instrument Cluster access.
Destination The final destination to which the vehicle will navigate.
ETA Estimated time of arrival at the destination.
Head Unit (HU) Primary computational unit embedded in a car. The HU runs all Android code and is connected to the central display in the car.
Instrument Cluster Secondary display located behind the steering wheel and between the car instruments. This can be an independent computational unit connected to the HU through the car's internal network (CAN bus) or a secondary display attached to the HU.
InstrumentClusterRenderingService Base class for the service used to interface with the Instrument Cluster display. OEMs must provide an extension of this class that interacts with the OEM-specific hardware.
KitchenSink app Test application included with Android Automotive.
Route A specific path along which a vehicle navigates to arrive at a destination.
Singleton service An Android service with the android:singleUser attribute. At any given time, at most one instance of the service runs on the Android system.

Prerequisites

To develop the integration, be sure to have these elements:

  • Android development environment. To set up the Android development environment, see Build requirements.
  • Download the Android source code. Get the latest version of the Android source code from the pi-car-release branch (or later) at https://android.googlesource.com.
  • Head Unit (HU). An Android device capable of running Android 9 (or later). This device must have its own display and be capable of flashing the display with new builds of Android.
  • Instrument Cluster is one of the following:
    • Physical secondary display attached to the HU. If the device hardware and kernel support the management of multiple displays.
    • Independent unit. Any computational unit connected to the HU via a network connection, capable of receiving and displaying a video stream on its own display.
    • Emulated display. During development, you can use one of these emulated environments:
      • Simulated secondary displays. To enable a simulated secondary display on any AOSP Android distribution, go to the Developer Options settings in the Settings system application and then select Simulate secondary displays. This configuration is equivalent to attaching a physical secondary display, with the limitation that this display is superimposed over the primary display.
      • Emulated instrument cluster. The Android emulator included with Android Automotive provides an option to display an instrument cluster with Android emulator _qemu-pipes. Use the DirectRenderingClusterSample reference instrument cluster implementation to connect to this emulated external display.

Integration Architecture

Integration Components

An integration of the Instrument Cluster API consists of these three components:

  • CarService
  • Navigation apps.
  • OEM Instrument Cluster Service

Integration components

CarService

CarService mediates between navigation apps and the car, ensuring that only one navigation app is active at any given time and only apps with the android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL permission can send data to the car.

CarService bootstraps all car-specific services and provides access to these services through a series of managers. To interact with the services, applications running in the car can access these managers.

For instrument cluster implementation, automotive OEMs must create a custom implementation of InstrumentClusterRendererService and update the config.xml file to point to that customized implementation.

When rendering an Instrument Cluster, during the boot process the CarService reads the InstrumentClusterRendererService key of the config.xml to locate an implementation of InstrumentClusterService. In AOSP, this entry points to the Navigation State API sample cluster implementation render service:

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

The service referred to in this entry is initialized and bound to CarService. When navigation apps, like Google Maps, request a CarInstrumentClusterManager, CarService provides a manager that updates the Instrument Cluster state from the bound InstrumentClusterRenderingService. (In this case, bound refers to Android Services.)

Instrument Cluster Service

OEMs must create an Android PacKage (APK) that contains a subclass of InstrumentClusterRendererService. See ClusterRenderingService for a sample.

This class serves two purposes:

  • Provides an interface between Android and the Instrument Cluster rendering device (the purpose of this document).
  • Receives and renders navigation state updates, such as turn-by-turn navigation guidance.

For the first purpose, OEM implementations of InstrumentClusterRendererService must initialize the secondary display used to render information on screens in the car cabin and communicate this information to CarService by calling to the InstrumentClusterRendererService.setClusterActivityOptions() and InstrumentClusterRendererService.setClusterActivityState() methods.

For the second function, the Instrument Cluster service must provide an implementation of the NavigationRenderer interface that receives navigation status update events, which are encoded as an eventType and event data encoded in a bundle.

Integration Sequence

The following diagram illustrates the implementation of a navigation state that renders updates:

Integration sequence

Legend:

  • Yellow. CarService and CarNavigationStatusManager provided by the Android platform.
  • Cyan. InstrumentClusterRendererService implemented by the OEM.
  • Purple. The Navigation app implemented by Google and third-party developers.
  • Green. CarAppFocusManager.

The Navigation State information flow follows this sequence:

  1. CarService initializes the InstrumentClusterRenderingService.
  2. During initialization, the InstrumentClusterRenderingService updates CarService with:
    1. Instrument Cluster display properties, such as unobscure boundaries (see more details about unobscure boundaries later).
    2. Activity Options needed to launch activities inside the Instrument Cluster display (see more details at ActivityOptions.
  3. A navigation app (such as Google Maps for Android Automotive or any maps app with the required permissions):
    1. Obtains a CarAppFocusManager using the Car class from car-lib.
    2. Before turn-by-turn directions start, calls to CarAppFocusManager.requestFocus() to pass CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION as the appType parameter.
  4. CarAppFocusManager communicates this request to CarService. If granted, CarService inspects the navigation app package and locates an activity marked with category android.car.cluster.NAVIGATION.
  5. If found, the navigation app uses the ActivityOptions reported by the InstrumentClusterRenderingService to launch the activity and includes the Instrument Cluster display properties as extras in the intent.

Integrating the API

The InstrumentClusterRenderingService implementation must:

  • Be designated as a singleton service by adding the following value to the AndroidManifest.xml. This is necessary to ensure that a single copy of the Instrument Cluster service will run, even during initialization and user switching:
    android:singleUser="true"
  • Hold the BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE system permission. This guarantees that only the Instrument Cluster rendering service included as part of the Android system image is ever bound by the CarService.
    <uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>

Implementing InstrumentClusterRenderingService

To build the service:

  1. Write a class that extends from InstrumentClusterRenderingService and then add a corresponding entry to your AndroidManifest.xml file. This class controls the Instrument Cluster display and can (optionally) render Navigation State API data.
  2. During onCreate(), use this service to initialize the communication with the rendering hardware. Options include:
    • Determine the secondary display to be used for the Instrument Cluster.
    • Create a virtual display so that the Instrument Cluster app renders and transmits the rendered image to an external unit (using a video streaming format such as H.264).
  3. When the display indicated above is ready, this service must call InstrumentClusterRenderingService#setClusterActivityLaunchOptions() to define the exact ActivityOptions that must be used to display an Activity on the Instrument Cluster. Use these parameters:
    • category. CarInstrumentClusterManager#CATEGORY_NAVIGATION
    • ActivityOptions. An ActivityOptions instance that can be used to launch an Activity in the Instrument Cluster. For example, from the sample Instrument Cluster implementation on AOSP:
      getService().setClusterActivityLaunchOptions(
         CATEGORY_NAVIGATION,
         ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
      
    • When the Instrument Cluster is ready to display activities, this service must invoke:
      InstrumentClusterRenderingService#setClusterActivityState().

      Use these parameters:
      • category. CarInstrumentClusterManager#CATEGORY_NAVIGATION
      • state. Bundle generated with ClusterActivityState. You must provide the following data:
        • visible. Specifies the Instrument Cluster as visible and ready to display content.
        • unobscuredBounds. A rectangle that defines the area within the Instrument Cluster display in which it's safe to display content. For example, areas covered by dials and gauges.
    • Override the Service#dump() method and report status information useful for debugging (see dumpsys for more information).

Sample InstrumentClusterRenderingService Implementation

The following example outlines an InstrumentClusterRenderingService implementation, which creates a VirtualDisplay to present the Instrument Cluster content on a remote physical display.

Alternatively, as mentioned above, alternatively this code could pass the displayId of a physical secondary display connected to the HU, if one is known to be available.

/**
* Sample {@link InstrumentClusterRenderingService} implementation
*/
public class SampleClusterServiceImpl extends InstrumentClusterRenderingService {
   // Used to retrieve or create displays
   private final DisplayManager mDisplayManager;
   // Unique identifier for the display that will 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);
  }
}

Appendix: Using the Sample Application

AOSP provides a sample application that implements the Navigation State API.

To run this sample application:

  1. Build and flash Android Auto on a supported HU. Use the Android building and flashing instructions specific to your device. For instructions, see Using Reference Boards.
  2. Connect a physical secondary display to the HU (if supported) or turn on the virtual secondary HU:
    1. Select Developer Mode in the Settings app.
    2. Go to Settings > System > Advanced > Developer options > Simulate secondary displays.
  3. Reboot the HU. The ClusterRenderingService service is connected to the secondary display.
  4. To launch the KitchenSink app:
    1. Open the drawer.
    2. Go to Inst. Cluster.
    3. Click START METADATA.

KitchenSink requests NAVIGATION focus, which instructs the DirectRenderingCluster service to display a mocked-up user interface on the Instrument Cluster.