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 page describes how to create a
service to control that secondary display and to then integrate the service with
so that navigation apps can display a user interface.CarService
Terminology
The following terms are used on this page:
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 ClusterRenderingService service is connected to the secondary display. emulator _qemu-pipes. ClusterRenderingService service is connected to the secondary display.
reference instrument cluster implementation to connect to this emulated external display.
Integration architecture
Integration components
Any integration of the Instrument Cluster API consists of these three components:
CarService
- Navigation apps
- OEM Instrument Cluster Service
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 ClusterRenderingService service is connected to the secondary display.
When rendering an Instrument Cluster, during the boot process the
CarService
reads the InstrumentClusterRendererService
key of the
ClusterRenderingService service is connected to the secondary display.
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 ClusterRenderingService service is connected to the secondary display. ClusterRenderingService service is connected to the secondary display. for a sample.
This class serves two purposes:
- Provides an interface Android and the Instrument Cluster rendering device (the purpose of this page).
- 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
ClusterRenderingService service is connected to the secondary display.
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:
In this illustration, colors denote the following:
- Yellow.
CarService
andCarNavigationStatusManager
provided by the Android platform. To learn more, see Car and CAR_NAVIGATION_SERVICE. - Cyan.
InstrumentClusterRendererService
implemented by the OEM. - Purple. The Navigation app implemented by Google and third-party developers.
- Green.
CarAppFocusManager
. To learn more, see Using the CarAppFocusManager API below and CarAppFocusManager.
The Navigation State information flow follows this sequence:
CarService
initializes theInstrumentClusterRenderingService
.- During initialization, the
InstrumentClusterRenderingService
updatesCarService
with:- Instrument Cluster display properties, such as unobscure boundaries (see more details about unobscure boundaries later).
- Activity options needed to launch activities inside the Instrument Cluster display (see more details at ActivityOptions.
- A navigation app (such as Google Maps for Android Automotive or any maps app
with the required permissions):
- Obtains a
CarAppFocusManager
using the Car class from car-lib. - Before turn-by-turn directions start, calls to
CarAppFocusManager.requestFocus()
to passCarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
as theappType
parameter.
- Obtains a
CarAppFocusManager
communicates this request toCarService
. If granted,CarService
inspects the navigation app package and locates an activity marked with categoryandroid.car.cluster.NAVIGATION
.- If found, the navigation app uses the
ActivityOptions
reported by theInstrumentClusterRenderingService
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 theCarService
:<uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
Implementing InstrumentClusterRenderingService
To build the service:
- Write a class that extends from ClusterRenderingService service is connected to the secondary display. and then add a corresponding entry to your
- 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).
- When the display indicated above is ready, this service must call
InstrumentClusterRenderingService#setClusterActivityLaunchOptions()
to define the exactActivityOptions
that must be used to display an Activity on the Instrument Cluster. Use these parameters:- category. ClusterRenderingService service is connected to the secondary display.
ActivityOptions.
AnActivityOptions
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
ClusterRenderingService service is connected to the secondary display.state
Bundle generated with ClusterRenderingService service is connected to the secondary display.
Be sure to 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).
AndroidManifest.xml
file. This class
controls the Instrument Cluster display and can (optionally) render Navigation State
API data.
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, 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); } }
Using the CarAppFocusManager API
The CarAppFocusManager API provides a method named getAppTypeOwner()
, which allows the
cluster service written by OEMs to know which navigation app has navigation focus at any given time.
OEMs can use the existing CarAppFocusManager#addFocusListener()
method, and then use
getAppTypeOwner()
to learn which app has focus. With this information, OEMs can:
- Switch the activity shown in the cluster to the cluster activity provided by the navigation app holding focus.
- Can detect if the focused navigation app has a cluster activity or not. If the focused navigation app doesn't have a cluster activity (or if such activity is disabled), OEMs can send this signal to the car DIM so the navigation facet of the cluster is skipped altogether.
Use CarAppFocusManager
to set and listen for the current application focus, such as
active navigation or a voice command. Usually only one instance of such an application is actively
running (or focused) in the system.
Use the CarAppFocusManager#addFocusListener(..)
method to listen for app focus
changes:
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 }
Use the CarAppFocusManager#getAppTypeOwner(..)
method to retrieve the package
names of the current owner of a given application type that is in focus. This method may return
more than one package name if the current owner uses the android:sharedUserId
feature.
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 application 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 } ...
Appendix: Using the sample application
AOSP provides a sample application that implements the Navigation State API.
To run this sample application:
- 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.
- Connect a physical secondary display to the HU (if supported) or turn on the
virtual secondary HU:
- Select Developer Mode in the Settings app.
- Go to Settings > System > Advanced > Developer options > Simulate secondary displays.
- Reboot the HU. The ClusterRenderingService service is connected to the secondary display.
- To launch the KitchenSink app:
- Open the drawer.
- Go to Inst. Cluster.
- Click START METADATA.
KitchenSink requests NAVIGATION focus, which instructs the DirectRenderingCluster
service to display a mocked-up user interface on the Instrument Cluster.