Instrument Cluster API

Instrument Cluster API(一款 Android API)可在车载辅助显示设备(如位于方向盘后方的仪表盘上的辅助显示设备)上显示导航应用,包括 Google 地图。本页面介绍了如何创建 来控制辅助显示设备,并将服务与 CarService,以便导航应用可以显示 界面。

术语

本页中使用了以下术语。

CarInstrumentClusterManager
使外部应用能够在上面启动 activity 的 CarManager 实例 仪表板,并在仪表板准备好显示时接收回调 活动。
CarManager
外部应用用来与汽车专用进行交互的所有管理器的基类 由 CarService 实现的服务。
CarService
在外部应用之间提供通信的 Android 平台服务 (包括 Google 地图)和汽车专用功能,例如仪表板访问权限。
目的地
车辆将导航到的最终目的地。
预计到达时间 (ETA)
预计到达目的地的时间。
车机 (HU)
车内嵌入的主要计算单元。HU 运行所有 Android 代码, 已连接到车内中央显示屏
仪表板
位于方向盘后方车辆之间的辅助显示屏 instruments。这可以是通过汽车内部网络(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 的任何计算单元,能够接收并在其显示屏上显示视频串流。
    • 模拟显示屏。在开发过程中,您可以使用以下模拟环境之一:
      • 模拟辅助显示设备。要启用模拟 辅助显示屏,请前往开发者选项 设置系统应用中的设置,然后选择模拟辅助设备 display此配置相当于将 但此显示屏叠加在主显示屏上 。
      • 模拟仪表板。包含的 Android 模拟器 AAOS 提供了显示仪表板和 ClusterRenderingService

集成架构

集成组件

Instrument Cluster API 的任何集成都包括以下三个组件:

  • CarService
  • 导航应用
  • OEM 仪表板服务

集成组件

CarService

CarService 可在导航应用与汽车之间进行协调,确保在任何时候都只有一个导航应用处于活跃状态,并且只有具有 android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL 权限的应用才能向汽车发送数据。

CarService 可以启动所有汽车特有服务,并通过一系列管理器提供对这些服务的访问。要与这些服务进行交互 在汽车中运行的应用可以访问这些管理器。

对于仪表板实现,汽车 OEM 必须创建自定义 并更新 ClusterRenderingService

呈现仪表板时,在启动过程中 CarService 读取 InstrumentClusterRendererServiceClusterRenderingService 找到 InstrumentClusterService 的实现。在 AOSP 中,此条目指向导航状态 API 集群实现呈现服务示例:

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

此条目中引用的服务经初始化处理并绑定到 CarService。当导航应用(如 Google 地图)请求 CarInstrumentClusterManager 时,CarService 会提供一个管理器,用于根据绑定的 InstrumentClusterRenderingService 更新仪表板状态。(在这种情况下,“绑定的服务”指的是 Android 服务。)

仪表板服务

OEM 必须创建一个 Android 软件包 (APK),其中包含 ClusterRenderingService

此类有以下两个用途:

  • 提供 Android 和仪表板呈现设备的接口 (本页的目的)。
  • 接收并呈现导航状态更新,例如精细导航 导航指导。

为实现第一个用途,OEM 的 InstrumentClusterRendererService 实现必须初始化用于在车厢内屏幕上呈现信息的辅助显示设备,并通过调用 InstrumentClusterRendererService.setClusterActivityOptions()InstrumentClusterRendererService.setClusterActivityState() 方法将此信息传达给 CarService

对于第二项功能,仪表板服务必须提供一个 实施 ClusterRenderingService 用于接收导航状态更新事件的接口,这些事件会编码为 eventType 和事件数据编码到 bundle 中。

集成序列

下图展示了呈现更新的导航状态的实现:

集成序列

在此图中,各种颜色的含义如下:

  • 黄色:由 Android 平台提供的 CarServiceCarNavigationStatusManager。如需了解详情,请参阅 CarCAR_NAVIGATION_SERVICE
  • 青色:由 OEM 实现的 InstrumentClusterRendererService
  • 紫色。由 Google 和第三方开发者实现的导航应用。
  • 绿色CarAppFocusManager。如需了解详情,请参阅下文的使用 CarAppFocusManager APICarAppFocusManager

导航状态信息流程遵循以下序列:

  1. CarService 初始化 InstrumentClusterRenderingService
  2. 在初始化期间,InstrumentClusterRenderingService 使用以下选项更新 CarService
    1. 仪表板屏幕属性,如无遮挡边界(可稍后了解有关无遮挡边界的更多详情)。
    2. 在仪表板显示屏内启动 activity 所需的 activity 选项。 如需了解详情,请参阅 ActivityOptions
  3. 导航应用(例如适用于 Android Automotive 的 Google 地图或任何地图应用) 具有所需权限): <ph type="x-smartling-placeholder">
      </ph>
    1. 使用 Car 类从 car-lib 中获取 CarAppFocusManager
    2. 在启动精细导航路线之前, CarAppFocusManager.requestFocus()即可通过 CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION作为appType 参数。
  4. CarAppFocusManager 会将此请求传达给 CarService。在授予条件下,CarService 会检查导航应用软件包,并定位标有类别 android.car.cluster.NAVIGATION 的 activity。
  5. 找到后,导航应用会使用 ActivityOptions InstrumentClusterRenderingService,用于启动 activity 并包含 仪表板屏幕属性作为 intent 中的 extra。

集成 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

如需构建服务,请执行以下操作:

  1. 编写一个从 ClusterRenderingService 然后,在 AndroidManifest.xml 文件中添加相应的条目。此课程 控制仪表板显示屏,并(可选)呈现导航状态 API 数据。
  2. onCreate() 期间,使用此服务初始化与 渲染硬件选项包括:
    • 确定用于仪表板的辅助显示设备。
    • 创建一个虚拟屏幕,以便仪表板应用渲染和传输 将渲染的图像发送到外部单元(使用 H.264 等视频串流格式)。
  3. 当上述屏幕准备就绪后,此服务必须调用 InstrumentClusterRenderingService#setClusterActivityLaunchOptions() 才能对在仪表板上显示 activity 必须使用的确切 ActivityOptions 进行定义。请使用以下参数:
    • category. ClusterRenderingService
    • ActivityOptions.。一个 ActivityOptions 实例,可以用于在仪表板中启动 activity。例如,从示例 AOSP 上的仪表板实现:
      getService().setClusterActivityLaunchOptions(
        CATEGORY_NAVIGATION,
        ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
      
  4. 当仪表板准备好显示 activity 时,此服务必须调用 InstrumentClusterRenderingService#setClusterActivityState()。请使用以下参数:
    • category ClusterRenderingService
    • 生成了 state 个套装 ClusterRenderingService。 请务必提供以下数据: <ph type="x-smartling-placeholder">
        </ph>
      • visible。用于将仪表板指定为可见,并准备好显示内容。
      • unobscuredBounds。一个矩形,用于定义仪表板屏幕内可以安全显示内容的区域。例如,表盘和量表覆盖的区域。
  5. 替换 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 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

CarAppFocusManager API 提供了一个名为 getAppTypeOwner() 的方法,该方法允许 OEM 编写的仪表板服务,用于了解哪个导航应用在任意给定位置具有导航焦点 。OEM 可以使用现有的 CarAppFocusManager#addFocusListener() 方法;以及 然后使用 getAppTypeOwner() 了解获得焦点的应用。有了这些信息 OEM 可以:

  • 将仪表板中显示的 activity 切换到导航应用提供的仪表板 activity 保持焦点
  • 检测聚焦的导航应用是否有仪表板 activity。如果聚焦的 导航应用没有仪表板 activity(或者此类 activity 已停用),OEM 可以 将此信号发送到汽车 DIM,以便完全跳过仪表板的导航分面。

使用 CarAppFocusManager 设置和监听当前应用焦点,例如 当前导航或语音指令。此类应用通常只有一个实例处于活动状态 在系统中运行的(或聚焦的)。

使用 CarAppFocusManager#addFocusListener(..) 方法监听应用焦点更改:

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
}

使用 CarAppFocusManager#getAppTypeOwner(..) 方法检索软件包 处于焦点的给定应用类型的当前所有者的名称。如果当前所有者使用了 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
}

...

附录:使用示例应用

AOSP 提供了一个实现 Navigation State API 的示例应用。

如需运行此示例应用,请执行以下操作:

  1. 在受支持的 HU 上构建并刷写 Android Auto。使用适用于您的设备的 Android 构建和刷写说明。如需了解相关说明,请参阅使用参考开发板
  2. 将实体辅助显示设备连接到 HU(如果支持)或开启虚拟显示设备 辅助 HU: <ph type="x-smartling-placeholder">
      </ph>
    1. 在“设置”应用中,选择开发者模式
    2. 转到设置 >系统 >高级 >开发者选项 > 模拟辅助显示屏
  3. 重新启动 HU
  4. 如需启动 KitchenSink 应用,请执行以下操作:
    1. 打开抽屉式导航栏。
    2. 转到仪表板
    3. 点击启动元数据

KitchenSink 会请求导航焦点,此举会指示 DirectRenderingCluster 服务在仪表板上显示模拟界面。