儀錶盤

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

術語

本頁使用以下術語:

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

先決條件

要開發集成,請確保具有以下元素:

  • 安卓開發環境。要設置 Android 開發環境,請參閱構建要求
  • 下載安卓源代碼。https://android.googlesource.com的 pi-car-release 分支(或更高版本)獲取最新版本的 Android 源代碼。
  • 主機 (HU)。能夠運行 Android 9(或更高版本)的 Android 設備。此設備必須有自己的顯示屏,並且能夠使用新版本的 Android 閃爍顯示屏。
  • 儀表組是以下之一:
    • 連接到 HU 的物理輔助顯示器。如果設備硬件和內核支持多顯示器的管理。
    • 獨立單位。通過網絡連接到 HU 的任何計算單元,能夠在其自己的顯示器上接收和顯示視頻流。
    • 模擬顯示。在開發過程中,您可以使用以下模擬環境之一:
      • 模擬輔助顯示器。要在任何 AOSP Android 發行版上啟用模擬輔助顯示器,請轉到設置系統應用程序中的開發人員選項設置,然後選擇模擬輔助顯示器。此配置等效於附加物理輔助顯示器,但限制是此顯示器疊加在主顯示器上。
      • 模擬儀錶盤。 Android Automotive 附帶的 Android 模擬器提供了一個選項,可以使用Android 模擬器 _qemu-pipes顯示儀表板。使用DirectRenderingCluster參考儀錶盤實現連接到這個模擬的外部顯示器。

集成架構

集成組件

Instrument Cluster API 的任何集成都包含以下三個組件:

  • CarService
  • 導航應用
  • OEM 儀錶盤服務

集成組件

汽車服務

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

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

對於儀表板實施,汽車 OEM 必須創建 InstrumentClusterRendererService 的自定義實施並更新config.xml文件以指向該自定義實施。

渲染 Instrument Cluster 時,在啟動過程中CarService讀取config.xmlInstrumentClusterRendererService鍵以定位InstrumentClusterService的實現。在 AOSP 中,此條目指向 Navigation State API 示例集群實現渲染服務:

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

此條目中引用的服務已初始化並綁定到CarService 。當導航應用程序(如 Google 地圖)請求CarInstrumentClusterManager時, CarService提供一個管理器,該管理器會從綁定的InstrumentClusterRenderingService更新 Instrument Cluster 狀態。 (在這種情況下, bound指的是Android Services 。)

儀錶盤服務

OEM 必須創建一個包含InstrumentClusterRendererService .有關示例,請參閱ClusterRenderingService

這個類有兩個目的:

  • 提供一個接口 Android 和 Instrument Cluster 渲染設備(本頁的目的)。
  • 接收和呈現導航狀態更新,例如逐嚮導航引導。

對於第一個目的, InstrumentClusterRendererService的 OEM 實現必須初始化用於在車廂內的屏幕上呈現信息的輔助顯示器,並通過調用InstrumentClusterRendererService.setClusterActivityOptions()InstrumentClusterRendererService.setClusterActivityState()方法將此信息傳達給CarService

對於第二個功能,Instrument Cluster 服務必須提供NavigationRenderer接口的實現,該接口接收導航狀態更新事件,這些事件被編碼為eventType ,事件數據被編碼在一個包中。

積分序列

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

積分序列

在此插圖中,顏色表示以下內容:

  • 黃色。 Android 平台提供的CarServiceCarNavigationStatusManager
  • 青色。由 OEM 實現的InstrumentClusterRendererService
  • 紫色的。由谷歌和第三方開發者實現的導航應用。
  • 綠色的。 CarAppFocusManager

導航狀態信息流遵循以下順序:

  1. CarService初始化InstrumentClusterRenderingService
  2. 在初始化期間, InstrumentClusterRenderingService更新CarService
    1. 儀表板顯示屬性,例如清晰的邊界(稍後請參閱有關清晰邊界的更多詳細信息)。
    2. 在儀表板顯示中啟動活動所需的活動選項(請參閱ActivityOptions中的更多詳細信息。
  3. 導航應用程序(例如 Google Maps for Android Automotive 或任何具有所需權限的地圖應用程序):
    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來啟動 Activity,並將 Instrument Cluster 顯示屬性作為附加內容包含在 Intent 中。

集成 API

InstrumentClusterRenderingService實現必須:

  • 通過將以下值添加到 AndroidManifest.xml,將其指定為單例服務。這是確保儀表集群服務的單個副本運行所必需的,即使在初始化和用戶切換期間也是如此:
    android:singleUser="true"
  • 持有BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE系統權限。這保證了只有作為 Android 系統映像的一部分包含的 Instrument Cluster 渲染服務才會被CarService綁定:
    <uses-permission android:name="android.car.permission.BIND_INSTRUMENT_CLUSTER_RENDERER_SERVICE"/>
    

實現 InstrumentClusterRenderingService

要構建服務:

  1. 編寫一個從InstrumentClusterRenderingService擴展的類,然後將相應的條目添加到您的AndroidManifest.xml文件中。此類控制儀表板顯示,並且可以(可選)呈現導航狀態 API 數據。
  2. onCreate()期間,使用此服務初始化與渲染硬件的通信。選項包括:
    • 確定要用於組合儀表的輔助顯示器。
    • 創建一個虛擬顯示器,以便儀表板應用程序渲染渲染的圖像並將其傳輸到外部設備(使用視頻流格式,例如 H.264)。
  3. 當上述顯示準備就緒時,此服務必須調用InstrumentClusterRenderingService#setClusterActivityLaunchOptions()來定義必須用於在儀表板上顯示活動的確切ActivityOptions 。使用這些參數:
    • 類別。 CarInstrumentClusterManager#CATEGORY_NAVIGATION
    • ActivityOptions.一個ActivityOptions實例,可用於在 Instrument Cluster 中啟動 Activity。例如,來自 AOSP 上的示例 Instrument Cluster 實現:
      getService().setClusterActivityLaunchOptions(
         CATEGORY_NAVIGATION,
         ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
      
  4. 當儀表組準備好顯示活動時,此服務必須調用InstrumentClusterRenderingService#setClusterActivityState() 。使用這些參數:
    • category CarInstrumentClusterManager#CATEGORY_NAVIGATION
    • 使用ClusterActivityState生成的state包。請務必提供以下數據:
      • visible將儀表板指定為可見並準備好顯示內容。
      • unobscuredBounds一個矩形,用於定義儀表板顯示中可以安全顯示內容的區域。例如,刻度盤和儀表所覆蓋的區域。
  5. 覆蓋Service#dump()方法並報告對調試有用的狀態信息(有關詳細信息,請參閱dumpsys )。

示例 InstrumentClusterRenderingService 實現

以下示例概述了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 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);
  }
}

使用 CarAppFocusManager

CarAppFocusManager API 提供了一個名為getAppTypeOwner()的方法,它允許 OEM 編寫的集群服務在任何給定時間知道哪個導航應用程序具有導航焦點。 OEM 可以使用現有的CarAppFocusManager#addFocusListener()方法,然後使用getAppTypeOwner()來了解哪個應用程序具有焦點。有了這些信息,OEM 可以:

  • 將集群中顯示的活動切換為導航應用程序提供的集群活動。
  • 可以檢測焦點導航應用程序是否具有集群活動。如果重點導航應用程序沒有集群活動(或者如果此類活動被禁用),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 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
}

...

附錄:使用示例應用程序

AOSP 提供了一個實現導航狀態 API 的示例應用程序。

要運行此示例應用程序:

  1. 在受支持的 HU 上構建和刷寫 Android Auto。使用特定於您設備的 Android 構建和刷機說明。有關說明,請參閱使用參考板
  2. 將物理輔助顯示器連接到 HU(如果支持)或打開虛擬輔助 HU:
    1. 在設置應用程序中選擇開發人員模式
    2. 轉到設置 > 系統 > 高級 > 開發人員選項 > 模擬輔助顯示器
  3. 重新啟動 HU。 ClusterRenderingService服務連接到輔助顯示器。
  4. 要啟動 KitchenSink 應用程序:
    1. 打開抽屜。
    2. 研究所。集群
    3. 單擊開始元數據

KitchenSink 請求 NAVIGATION 焦點,它指示DirectRenderingCluster服務在 Instrument Cluster 上顯示模擬用戶界面。