儀表板 API

使用 Instrument Cluster API (Android API),即可在汽車的次要螢幕上顯示導航應用程式 (包括 Google 地圖),例如儀表板上方向盤後方。本頁面說明如何建立服務來控制次要螢幕,並將服務與 CarService 整合,以便導航應用程式顯示使用者介面。

術語

本頁使用的術語如下。

CarInstrumentClusterManager
CarManager 的例項,可讓外部應用程式在儀錶板上啟動活動,並在儀錶板準備顯示活動時接收回呼。
CarManager
外部應用程式用於與 CarService 實作之車輛專屬服務互動的所有管理工具的基礎類別。
CarService
Android 平台服務,可在外部應用程式 (包括 Google 地圖) 與車輛專屬功能 (例如儀表板存取權) 之間提供通訊。
到達網頁
車輛最終要前往的目的地。
預計到達時間 (ETA)
預估抵達目的地所需的時間。
車用運算主機 (HU)
內嵌在車輛中的主要運算單元。HU 會執行所有 Android 程式碼,並連接至車輛的中央螢幕。
儀表板
次要螢幕位於方向盤後方,並位於車輛儀表板之間。這可能是透過車輛內部網路 (CAN 匯流排) 連線至車用主機的獨立運算單元,或是連接至車用主機的次要螢幕。
InstrumentClusterRenderingService
用於與儀表板螢幕介面的服務的基礎類別。原始設備製造商必須提供此類別的擴充功能,以便與原始設備製造商專屬硬體互動。
KitchenSink 應用程式
Android Automotive 隨附的測試應用程式。
路線
車輛行駛到目的地的特定路徑。
單例服務
具有 android:singleUser 屬性的 Android 服務。在任何特定時間點,Android 系統上最多只會執行一個服務例項。

必要條件

繼續操作之前,請務必備妥下列元素:

  • Android 開發環境如要設定 Android 開發環境,請參閱「建構需求」。
  • 下載 Android 原始碼。請前往 https://android.googlesource.com,從 pi-car-release 分支 (或更新版本) 取得最新版的 Android 原始碼。
  • 主機 (HU)。可執行 Android 9 (或以上版本) 的 Android 裝置。此裝置必須有專屬螢幕,且能夠透過新版 Android 刷新螢幕。
  • 「儀表板」是下列其中一種:
    • 連接至車用多媒體系統的實體次要螢幕。裝置硬體和核心是否支援多螢幕管理。
    • 獨立單位。透過網路連線連結至 HU 的任何運算單元,能夠在其螢幕上接收及顯示影片串流。
    • 模擬顯示畫面在開發期間,您可以使用下列其中一個模擬環境:
      • 模擬次要顯示器。如要在任何 AOSP Android 發行版本上啟用模擬的次要螢幕,請前往「設定」系統應用程式中的「開發人員選項」設定,然後選取「模擬次要螢幕」。此設定等同於連接實體次要螢幕,但限制是此螢幕會疊加在主要螢幕上。
      • 模擬儀表板。AAOS 隨附的 Android 模擬器提供選項,可透過 ClusterRenderingService 顯示儀表板。

整合架構

整合元件

任何儀表板 API 整合作業都包含下列三個元件:

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

整合元件

CarService

CarService 會在導航應用程式和車輛之間進行仲介,確保在任何特定時間只有一個導航應用程式處於啟用狀態,且只有具備 android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL 權限的應用程式才能將資料傳送至車輛。

CarService 會啟動所有車輛專屬服務,並透過一系列管理員提供這些服務的存取權。為了與服務互動,在車上執行的應用程式可以存取這些管理員。

針對儀表板群組實作,汽車原始設備製造商必須建立 InstrumentClusterRendererService 的自訂實作,並更新 ClusterRenderingService

在轉譯儀錶板時,CarService 會在啟動程序期間讀取 ClusterRenderingServiceInstrumentClusterRendererService 鍵,以找出 InstrumentClusterService 的實作項目。在 AOSP 中,這個項目會指向 Navigation State API 範例叢集實作項目的算繪服務:

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

這個項目中所提及的服務會經過初始化,並繫結至 CarService。當 Google 地圖等導航應用程式要求 CarInstrumentClusterManager 時,CarService 會提供管理員,從繫結的 InstrumentClusterRenderingService 更新儀表板狀態。(在此情況下,bound 是指 Android Services)。

儀錶板服務

OEM 必須建立 Android 套件 (APK),其中包含 ClusterRenderingService 的子類別。

這個類別有兩個用途:

  • 提供 Android 和儀表板轉譯裝置的介面 (本頁的目的)。
  • 接收並轉譯導航狀態更新,例如即時路線導航指引。

就第一個用途而言,InstrumentClusterRendererService 的 OEM 實作必須初始化用於在汽車車室內的螢幕上顯示資訊的次要顯示畫面,並透過呼叫 InstrumentClusterRendererService.setClusterActivityOptions()InstrumentClusterRendererService.setClusterActivityState() 方法,將這項資訊傳送至 CarService

針對第二個功能,儀表板叢集服務必須提供 ClusterRenderingService 介面的實作項目,以便接收導航狀態更新 事件,這些事件會編碼為 eventType,而事件資料則會編碼為套件。

整合順序

下圖說明如何實作可轉譯更新的導覽狀態:

整合順序

在這個插圖中,顏色代表以下意義:

  • 黃色。CarServiceCarNavigationStatusManager 由 Android 平台提供。詳情請參閱「車輛」和「CAR_NAVIGATION_SERVICE」。
  • 青色。InstrumentClusterRendererService 由 OEM 實作。
  • 紫色。由 Google 和第三方開發人員實作的導航應用程式。
  • 綠色。跳到 CarAppFocusManager 的位置。如需更多資訊,請參閱下方的「使用 CarAppFocusManager API」和 CarAppFocusManager

導覽狀態資訊流程的順序如下:

  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 擴充的類別,然後在 AndroidManifest.xml 檔案中新增對應的項目。這個類別會控管儀表板螢幕,並可視情況算繪 Navigation State API 資料。
  2. onCreate() 期間,請使用這項服務初始化與轉譯硬體的通訊。選項包括:
    • 判斷要用於儀表板的次要螢幕。
    • 建立虛擬顯示器,讓儀錶板應用程式算繪並將算繪圖傳輸至外部裝置 (使用 H.264 等影片串流格式)。
  3. 當上述顯示畫面就緒後,此服務必須呼叫 InstrumentClusterRenderingService#setClusterActivityLaunchOptions(),以定義必須用於在儀表板上顯示活動的確切 ActivityOptions。請使用下列參數:
    • category. ClusterRenderingService
    • ActivityOptions. 可用於在儀錶板中啟動活動的 ActivityOptions 例項。例如,在 Android 開放原始碼計畫的儀表板範例實作中:
      getService().setClusterActivityLaunchOptions(
        CATEGORY_NAVIGATION,
        ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
  4. 當儀表板群組準備好顯示活動時,此服務必須叫用 InstrumentClusterRenderingService#setClusterActivityState()。請使用下列參數:
    • category ClusterRenderingService
    • state 使用 ClusterRenderingService 產生的套件。請務必提供下列資料:
      • visible 指定儀表板可供查看,並準備顯示內容。
      • unobscuredBounds 矩形,定義儀表板螢幕中可安全顯示內容的區域。例如錶盤和儀表所覆蓋的區域。
  5. 覆寫 Service#dump() 方法,並回報有助於偵錯的狀態資訊 (詳情請參閱 dumpsys)。

儀表板轉換服務實作範例

以下範例概述 InstrumentClusterRenderingService 實作方式,會建立 VirtualDisplay,在遠端實體螢幕上顯示 Instrument Cluster 內容。

或者,如果已知有可用的實體次要螢幕,這個程式碼可以傳遞連接至 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 提供實作 Navigation State API 的範例應用程式。

如要執行這個範例應用程式,請按照下列步驟操作:

  1. 在支援的車用多媒體系統上建構及刷新 Android Auto。請使用裝置專屬的 Android 建構和閃燈操作說明。如需操作說明,請參閱「使用參考板」。
  2. 將實體次要螢幕連接至 HU (如有支援),或開啟虛擬次要 HU:
    1. 在「設定」應用程式中選取「開發人員模式」
    2. 依序前往「設定」>「系統」>「進階」>「開發人員選項」>「模擬次要螢幕」
  3. 重新啟動車用 HUD
  4. 如要啟動 KitchenSink 應用程式,請按照下列步驟操作:
    1. 開啟抽屜。
    2. 前往「Instance Cluster」
    3. 按一下「開始中繼資料」

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