儀表組API

使用 Instrument Cluster API(Android API)在汽車的輔助顯示器上(例如儀表板上的方向盤後面)顯示導航應用程序,包括 Google 地圖。本頁介紹如何建立服務來控制輔助顯示屏,然後將該服務與CarService集成,以便導航應用程式可以顯示使用者介面。

術語

本頁使用以下術語:

學期描述
CarInstrumentClusterManager一個CarManager實例,使外部應用程式能夠在儀表板上啟動活動,並在儀表板上準備好顯示活動時接收回調。
CarManager外部應用程式使用的所有管理器的基類,用於與CarService實現的汽車特定服務進行互動。
CarService Android 平台服務,提供外部應用程式(包括 Google 地圖)和汽車特定功能(例如儀表組存取)之間的通訊。
目的地車輛將導航到的最終目的地。
預計到達時間預計到達目的地的時間。
主機 (HU)嵌入汽車中的主要計算單元。 HU 運行所有 Android 程式碼,並連接到汽車的中央顯示器。
儀表組輔助顯示器位於方向盤後方和汽車儀表之間。它可以是透過汽車內部網路(CAN 總線)連接到 HU 的獨立運算單元,也可以是連接到 HU 的輔助顯示器。
InstrumentClusterRenderingService用於與儀表板顯示器互動的服務的基底類別。 OEM 必須提供此類的擴展,以便與 OEM 特定的硬體進行互動。
廚房水槽應用程式Android Automotive 隨附的測試應用程式。
路線車輛導航到達目的地的特定路徑。
單例服務具有android:singleUser屬性的 Android 服務。在任何給定時間,Android 系統上最多運行一個服務實例。

先決條件

要開發集成,請務必具備以下要素:

  • 安卓開發環境。若要設定 Android 開發環境,請參閱建置要求
  • 下載Android原始碼。從 pi-car-release 分支(或更高版本)取得最新版本的 Android 原始碼:https: //android.googlesource.com
  • 主機 (HU)。能夠運行 Android 9(或更高版本)的 Android 裝置。該設備必須有自己的顯示屏,並且能夠使用新版本的 Android 來刷新顯示屏。
  • 儀表組是以下之一:
    • 連接到 HU 的實體輔助顯示器。如果設備硬體和核心支援多顯示器的管理。
    • 獨立單位。透過網路連接連接到 HU 的任何運算單元,能夠在其自己的顯示器上接收和顯示視訊串流。
    • 模擬顯示。在開發過程中,您可以使用以下模擬環境之一:
      • 模擬輔助顯示器。若要在任何 AOSP Android 發行版上啟用類比輔助顯示器,請前往「設定」系統應用程式中的「開發人員選項」設置,然後選擇「類比輔助顯示器」。此配置相當於連接實體輔助顯示器,但限制是該顯示器疊加在主顯示器上。
      • 類比儀表組。 Android Automotive 隨附的 Android 模擬器提供了一個選項來顯示儀表板, ClusterRenderingService服務已連接到輔助顯示器。
      • 模擬器 _qemu-pipes 。 ClusterRenderingService服務已連接到輔助顯示器。連接到該類比外部顯示器的參考儀表組實作。

整合架構

整合組件

Instrument Cluster API 的任何整合都包含以下三個元件:

  • CarService
  • 導航應用程式
  • OEM儀表組服務

整合組件

汽車服務

CarService在導航應用程式和汽車之間進行協調,確保在任何給定時間只有一個導航應用程式處於活動狀態,並且只有具有android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL權限的應用程式才能向汽車發送資料。

CarService引導所有特定於汽車的服務,並透過一系列管理器提供對這些服務的存取。為了與服務交互,汽車中運行的應用程式可以存取這些管理器。

對於儀表組實現,汽車 OEM 必須建立 InstrumentClusterRendererService 的自訂實現,並更新連接到輔助顯示器的ClusterRenderingService服務。

渲染儀表叢集時,在啟動過程中, CarService會讀取連接到輔助顯示器的ClusterRenderingService服務的InstrumentClusterRendererService金鑰。找到InstrumentClusterService的實作。在 AOSP 中,此條目指向導航狀態 API 範例叢集實作渲染服務:

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

此條目中引用的服務已初始化並綁定至CarService 。當導覽應用程式(如 Google 地圖)請求CarInstrumentClusterManager時, CarService會提供一個管理器,用於從綁定的InstrumentClusterRenderingService更新儀表叢集狀態。 (在本例中, bound指的是Android Services 。)

儀表組服務

OEM 必須建立一個 Android 套件 (APK),其中包含連接到輔助顯示器的ClusterRenderingService服務的子類別。 ClusterRenderingService服務已連接到輔助顯示器。樣品。

這個類別有兩個目的:

  • 提供 Android 和 Instrument Cluster 渲染裝置的介面(本頁的目的)。
  • 接收並呈現導航狀態更新,例如逐嚮導航指導。

出於第一個目的, InstrumentClusterRendererService的 OEM 實作必須初始化用於在車艙螢幕上呈現資訊的輔助顯示器,並透過呼叫InstrumentClusterRendererService.setClusterActivityOptions()InstrumentClusterRendererService.setClusterActivityState()方法將此資訊傳達給CarService

對於第二個功能,Instrument Cluster 服務必須提供連接到輔助顯示器的ClusterRenderingService服務的實作。接收導航狀態更新事件的接口,這些事件被編碼為eventType和編碼在捆綁中的事件資料。

積分順序

下圖說明了呈現更新的導航狀態的實現:

積分順序

在此圖中,顏色表示以下含義:

  • 黃色的。 Android平台提供的CarServiceCarNavigationStatusManager 。要了解更多信息,請參閱汽車CAR_NAVIGATION_SERVICE
  • 青色。 InstrumentClusterRendererService由 OEM 實作。
  • 紫色的。由 Google 和第三方開發者實施的導航應用程式。
  • 綠色的。 CarAppFocusManager 。要了解更多信息,請參閱下面的使用 CarAppFocusManager APICarAppFocusManager

導航狀態資訊流遵循以下順序:

  1. CarService初始化InstrumentClusterRenderingService
  2. 在初始化期間, InstrumentClusterRenderingService使用下列內容更新CarService
    1. 儀表組顯示屬性,例如清晰邊界(請參閱稍後有關清晰邊界的更多詳細資訊)。
    2. 在儀錶板顯示器內啟動活動所需的活動選項(請參閱ActivityOptions中的更多詳細資訊)。
  3. 導航應用程式(例如適用於 Android Automotive 的 Google 地圖或任何具有所需權限的地圖應用程式):
    1. 使用 car-lib 中的 Car 類別來取得CarAppFocusManager
    2. 在逐步導航開始之前,請呼叫CarAppFocusManager.requestFocus()以將CarAppFocusManager.APP_FOCUS_TYPE_NAVIGATION作為appType參數傳遞。
  4. CarAppFocusManager將此請求傳達給CarService 。如果獲得批准, CarService將檢查導航應用程式套件並找到標有類別android.car.cluster.NAVIGATION活動。
  5. 如果找到,導航應用程式將使用InstrumentClusterRenderingService報告的ActivityOptions來啟動活動,並將儀表群集顯示屬性作為附加內容包含在意圖中。

整合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擴展的類別,將服務連接到輔助顯示器。
  2. 然後將相應的條目新增至您的AndroidManifest.xml檔案中。此類控制儀錶板顯示,並且可以(可選)呈現導航狀態 API 資料。
  3. onCreate()期間,使用此服務初始化與渲染硬體的通訊。選項包括:
    • 決定用於組合儀表的輔助顯示器。
    • 建立虛擬顯示器,以便儀錶板應用程式渲染並將渲染的影像傳輸到外部裝置(使用視訊串流格式,例如 H.264)。
  4. 當上面指示的顯示準備就緒時,此服務必須呼叫InstrumentClusterRenderingService#setClusterActivityLaunchOptions()來定義必須用於在儀表集群上顯示活動的確切ActivityOptions 。使用這些參數:
    • 類別。 ClusterRenderingService服務已連接到輔助顯示器。
    • ActivityOptions.一個ActivityOptions實例,可用來啟動儀錶板中的活動。例如,來自 AOSP 上的儀表叢集實作範例:
      getService().setClusterActivityLaunchOptions(
         CATEGORY_NAVIGATION,
         ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
      
  5. 當儀表群集準備好顯示活動時,服務必須呼叫InstrumentClusterRenderingService#setClusterActivityState() 。使用這些參數:
    • ClusterRenderingService服務category連接到輔助顯示器。
    • 使用ClusterRenderingService服務產生的state捆綁包已連接到輔助顯示器。
    • 請務必提供以下數據:
      • visible將組合儀表指定為可見並準備顯示內容。
      • unobscuredBounds定義儀錶板顯示器內可安全顯示內容的區域的矩形。例如,刻度盤和儀表覆蓋的區域。
  6. 覆蓋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 可以將此訊號傳送至汽車 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 提供了一個實作導航狀態 API 的範例應用程式。

要運行此範例應用程式:

  1. 在支援的 HU 上建置並刷新 Android Auto。使用特定於您的裝置的 Android 建置和刷新說明。有關說明,請參閱使用參考板
  2. 將實體輔助顯示器連接到 HU(如果支援)或開啟虛擬輔助 HU:
    1. 在「設定」應用程式中選擇「開發者模式」
    2. 前往“設定”>“系統”>“進階”>“開發人員選項”>“模擬輔助顯示器”
  3. 重新啟動 HU。 ClusterRenderingService服務連接到輔助顯示器。
  4. 要啟動 KitchenSink 應用程式:
    1. 打開抽屜。
    2. 研究所。簇
    3. 點選“開始元資料”

KitchenSink 要求 NAVIGATION 焦點,該焦點指示DirectRenderingCluster服務在儀錶板上顯示模擬的使用者介面。