Instrument Cluster API(一款 Android API)可在车载辅助显示设备(如位于方向盘后方的仪表盘上的辅助显示设备)上显示导航应用,包括 Google 地图。本文介绍如何创建服务以控制该辅助显示设备并将该服务与 CarService 集成,以便导航应用可以显示界面。
术语
本文中使用了以下术语:
术语 | 说明 |
---|---|
CarInstrumentClusterManager | 一款 CarManager,使外部应用能够在仪表板上启动 Activity,并在仪表板准备好显示 Activity 时接收回调。 |
CarManager | 所有管理器的基类,外部应用使用这些管理器与通过 CarService 实现的汽车特有服务进行交互。 |
CarService | 一种 Android 平台服务,可在外部应用(包括 Google 地图)与汽车特有功能(如仪表板访问)之间提供通信。 |
目的地 | 车辆将导航到的最终目的地。 |
预计到达时间 | 预计到达目的地的时间。 |
车机 (HU) | 车内嵌入的主要计算单元。HU 会运行所有 Android 代码,并连接到汽车中央显示屏。 |
仪表板 | 位于方向盘后方车载仪表之间的辅助显示设备。这可以是通过汽车内部网络(CAN 总线)连接到 HU 的独立计算单元,也可以是连接到 HU 的辅助显示设备。 |
InstrumentClusterRenderingService | 用于与仪表板显示屏连接的服务的基类。原始设备制造商 (OEM) 必须提供该类的扩展程序,以便与 OEM 特有硬件互动。 |
KitchenSink 应用 | Android Automotive 中包含的测试应用。 |
路线 | 车辆导航到达目的地所经的特定路径。 |
单例服务 | 一种具有 android:singleUser 属性的 Android 服务。在任何给定时间,最多只有一个服务实例在 Android 系统上运行。 |
前提条件
如需开发集成,请确保具备以下元素:
- Android 开发环境。如需设置 Android 开发环境,请参阅构建要求。
- 下载 Android 源代码。访问 https://android.googlesource.com,从 pi-car-release 分支(或更高版本)获取最新版 Android 源代码。
- 车机 (HU)。能够搭载 Android 9(或更高版本)的 Android 设备。此设备必须具有自己的显示屏,并且能够使用 Android 的新 build 刷写显示屏。
- 仪表板采用以下类型之一:
- 连接到 HU 的实体辅助显示设备。条件是设备硬件和内核支持对多个显示屏进行管理。
- 独立单元。通过网络连接连接到 HU 的任何计算单元,能够接收并在其显示屏上显示视频串流。
- 模拟显示屏。在开发过程中,您可以使用以下模拟环境之一:
- 模拟辅助显示设备。如需在任何发行版 AOSP Android 上启用模拟辅助显示设备,请转到“设置”系统应用中的“开发者选项”设置,然后选择“模拟辅助显示设备”。此配置相当于连接实体辅助显示设备,只不过此显示设备叠加显示在主显示设备之上。
- 模拟仪表板。Android Automotive 中包含的 Android 模拟器提供了一个选项,从而能够使用 Android 模拟器 _qemu-pipes 显示仪表板。使用 DirectRenderingClusterSample 仪表板参考实现连接到此模拟外部屏幕。
集成架构
集成组件
Instrument Cluster API 的集成包括以下三个组件:
- CarService
- 导航应用
- OEM 仪表板服务
CarService
CarService 可在导航应用与汽车之间进行协调,确保在任何时候只有一个导航应用处于活动状态,并且只有具有 android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL 权限的应用才能向汽车发送数据。
CarService 可以启动所有汽车特有服务,并通过一系列管理器提供对这些服务的访问。为了与服务进行互动,在汽车内运行的应用可以访问这些管理器。
对于仪表板实现,汽车 OEM 必须创建自定义的 InstrumentClusterRendererService 实现,并更新 config.xml 文件以指向该自定义实现。
当呈现仪表板时,CarService 会在启动过程中读取 config.xml 的 InstrumentClusterRendererService 密钥,以定位 InstrumentClusterService 实现。在 AOSP 中,此条目指向导航状态 API 集群实现呈现服务示例:
<string name="instrumentClusterRendererService"> android.car.cluster/.ClusterRenderingService </string>
此条目中引用的服务经初始化处理并绑定到 CarService。当导航应用(如 Google 地图)请求 CarInstrumentClusterManager 时,CarService 会提供一个管理器,用于根据绑定的 InstrumentClusterRenderingService 更新仪表板状态。(在这种情况下,“绑定的服务”指的是 Android 服务。)
仪表板服务
OEM 必须创建包含 InstrumentClusterRendererService 子类的 Android 软件包 (APK)。 如需查看示例,请参阅 ClusterRenderingService。
此类有以下两个用途:
- 提供 Android 与仪表板呈现设备之间的接口(本文档的用途)。
- 接收并呈现导航状态更新,如精细导航指导。
为实现第一个用途,OEM 的 InstrumentClusterRendererService 实现必须初始化用于在车厢内屏幕上呈现信息的辅助显示设备,并通过调用 InstrumentClusterRendererService.setClusterActivityOptions()
和 InstrumentClusterRendererService.setClusterActivityState()
方法将此信息传达给 CarService。
为实现第二个用途,仪表板服务必须提供 NavigationRenderer 接口的实现,该接口用于接收导航状态更新事件,这些事件在软件包中编码为 eventType 数据和事件数据。
集成序列
下图说明了呈现更新的导航状态的实现:
图例:
- 黄色。由 Android 平台提供的 CarService 和 CarNavigationStatusManager。
- 青色。由 OEM 实现的 InstrumentClusterRendererService。
- 紫色。由 Google 和第三方开发者实现的导航应用。
- 绿色。CarAppFocusManager。
导航状态信息流程遵循以下序列:
- CarService 初始化 InstrumentClusterRenderingService。
- 在初始化期间,InstrumentClusterRenderingService 使用以下选项更新 CarService:
- 仪表板屏幕属性,如无遮挡边界(可稍后了解有关无遮挡边界的更多详情)。
- 在仪表板屏幕内启动 Activity 所需要的 Activity 选项(如需了解更多详情,请参阅 ActivityOptions)。
- 导航应用(如适用于 Android Automotive 的 Google 地图或任何具有所需权限的地图应用):
- 使用 Car 类从 car-lib 中获取 CarAppFocusManager。
- 在启动精细导航路线之前,调用
CarAppFocusManager.requestFocus()
以将CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION
作为appType
参数传递。
- CarAppFocusManager 会将此请求传达给 CarService。在授予条件下,CarService 会检查导航应用软件包,并定位标有类别
android.car.cluster.NAVIGATION
的 Activity。 - 找到之后,导航应用会使用 InstrumentClusterRenderingService 报告的 ActivityOptions 启动 Activity,并在 intent 中包含仪表板屏幕属性作为附加内容。
集成 API
InstrumentClusterRenderingService 实现必须满足以下条件:
- 通过将以下值添加到 AndroidManifest.xml,将实现指定为单例服务。这是确保仪表板服务的单个副本可运行的前提条件,即使在初始化和用户切换期间也可运行:
android:singleUser="true"
- 具有
BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE
系统权限。这样可以确保只有 Android 系统映像中包含的仪表板呈现服务才由 CarService 绑定。
<uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
实现 InstrumentClusterRenderingService
如需构建服务,请执行以下操作:
- 编写一个从 InstrumentClusterRenderingService 扩展的类,然后向您的 AndroidManifest.xml 文件添加相应的条目。此类用于控制仪表板屏幕,并且可以呈现导航状态 API 数据(可选)。
- 在 onCreate() 期间,使用此服务初始化与呈现硬件之间的通信。选项包括:
- 确定用于仪表板的辅助显示设备。
- 创建一个虚拟屏幕,以便仪表板应用呈现图像并将呈现的图像传输到外部单元(使用 H.264 等视频串流格式)。
- 当上述屏幕准备就绪后,此服务必须调用 InstrumentClusterRenderingService#setClusterActivityLaunchOptions() 才能对在仪表板上显示 Activity 必须使用的确切 ActivityOptions 进行定义。请使用以下参数:
- category。CarInstrumentClusterManager#CATEGORY_NAVIGATION
- ActivityOptions。一个 ActivityOptions 实例,可以用于在仪表板中启动 Activity。例如,从 AOSP 上的仪表板实现示例中:
getService().setClusterActivityLaunchOptions( CATEGORY_NAVIGATION, ActivityOptions.makeBasic() .setLaunchDisplayId(displayId));
- 当仪表板准备好显示 Activity 时,此服务必须调用:
InstrumentClusterRenderingService#setClusterActivityState()
。
请使用以下参数:- category。CarInstrumentClusterManager#CATEGORY_NAVIGATION
- state。通过 ClusterActivityState 生成的软件包。
您必须提供以下数据:
- visible。用于将仪表板指定为可见,并准备好显示内容。
- unobscuredBounds。一个矩形,用于定义仪表板屏幕内可以安全显示内容的区域。例如,表盘和量表覆盖的区域。
- 用于替换
Service#dump()
方法并报告可用于调试的状态信息(如需了解详情,请参阅 dumpsys)。
InstrumentClusterRenderingService 实现示例
以下示例概述了 InstrumentClusterRenderingService 实现,这会创建一个 VirtualDisplay,用以在远程实体屏幕上显示仪表板内容。
或者,如上所述,如果已知连接到 HU 的实体辅助显示设备可用,此代码就可以传递该实体辅助显示设备的 displayId。
/** * 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); } }
附录:使用示例应用
AOSP 提供了一个实现导航状态 API 的示例应用。
如需运行此示例应用,请执行以下操作:
- 在受支持的 HU 上构建并刷写 Android Auto。使用适用于您的设备的 Android 构建和刷写说明。如需了解相关说明,请参阅使用参考板卡。
- 将实体辅助显示设备连接到 HU(如支持)或打开虚拟辅助 HU:
- 在“设置”应用中,选择开发者模式。
- 依次转到设置 > 系统 > 高级 > 开发者选项 > 模拟辅助显示设备。
- 重新启动 HU。ClusterRenderingService 服务连接到辅助显示设备。
- 如需启动 KitchenSink 应用,请执行以下操作:
- 打开抽屉式导航栏。
- 转到仪表板。
- 点击启动元数据。
KitchenSink 会请求导航焦点,此举会指示 DirectRenderingCluster 服务在仪表板上显示模拟界面。