Instrument Cluster API

Instrument Cluster API (Android API) を使用して、Google マップなどのナビゲーション アプリを、インストルメント パネルのステアリング ホイールの後ろなど、車内のセカンダリ ディスプレイに表示します。このページでは、セカンダリ ディスプレイを制御するサービスを作成し、そのサービスをCarServiceと統合して、ナビゲーション アプリがユーザー インターフェイスを表示できるようにする方法について説明します。

用語

このページでは次の用語が使用されます。

学期説明
CarInstrumentClusterManager外部アプリが計器クラスタ上でアクティビティを起動し、計器クラスタがアクティビティを表示する準備ができたときにコールバックを受信できるようにするCarManagerインスタンス。
CarManager CarServiceによって実装された自動車固有のサービスと対話するために外部アプリによって使用されるすべてのマネージャーの基本クラス。
CarService外部アプリ (Google マップを含む) と、インストルメント クラスター アクセスなどの自動車固有の機能との間の通信を提供する Android プラットフォーム サービス。
行き先車両が移動する最終目的地。
到着予定時刻目的地への到着予定時刻。
ヘッドユニット(HU)自動車に組み込まれた主要な計算ユニット。 HU はすべての Android コードを実行し、車内の中央ディスプレイに接続されます。
計器クラスタセカンダリ ディスプレイはステアリング ホイールの後ろ、車の計器の間にあります。これは、自動車の内部ネットワーク (CAN バス) を介して HU に接続された独立した計算ユニット、または HU に接続されたセカンダリ ディスプレイにすることができます。
InstrumentClusterRenderingService計器クラスタ ディスプレイとのインターフェイスに使用されるサービスの基本クラス。 OEM は、OEM 固有のハードウェアと対話するこのクラスの拡張機能を提供する必要があります。
キッチンシンクアプリAndroid Automotive に含まれるテスト アプリ。
ルート車両が目的地に到着するために移動する特定の経路。
シングルトンサービスandroid:singleUser属性を持つ Android サービス。 Android システム上では、常にサービスの最大 1 つのインスタンスが実行されます。

前提条件

統合を開発するには、次の要素が必要です。

  • Android開発環境。 Android 開発環境をセットアップするには、 「ビルド要件」を参照してください。
  • Android のソースコードをダウンロードします。 https://android.googlesource.comの pi-car-release ブランチ (またはそれ以降) から Android ソース コードの最新バージョンを入手します。
  • ヘッドユニット (HU)。 Android 9 (以降) を実行できる Android デバイス。このデバイスには独自のディスプレイがあり、Android の新しいビルドでディスプレイをフラッシュできる必要があります。
  • インストルメント クラスタは次のいずれかです。
    • HU に接続された物理的なセカンダリ ディスプレイ。デバイスのハードウェアとカーネルが複数のディスプレイの管理をサポートしている場合。
    • 独立したユニット。ネットワーク接続を介して HU に接続され、ビデオ ストリームを受信して​​独自のディスプレイに表示できる計算ユニット。
    • エミュレートされた表示。開発中は、次のエミュレートされた環境のいずれかを使用できます。
      • シミュレートされたセカンダリ ディスプレイ。 AOSP Android ディストリビューションでシミュレートされたセカンダリ ディスプレイを有効にするには、設定システム アプリの開発者向けオプション設定に移動し、[セカンダリ ディスプレイをシミュレートする] を選択します。この構成は、物理的なセカンダリ ディスプレイを接続するのと同じですが、このディスプレイがプライマリ ディスプレイに重ねられるという制限があります。
      • エミュレートされた計器クラスタ。 Android Automotive に含まれる Android エミュレータには、 ClusterRenderingServiceサービスがセカンダリ ディスプレイに接続されているインストルメント クラスタを表示するオプションが用意されています。
      • エミュレータ _qemu-pipes 。 ClusterRenderingServiceサービスはセカンダリ ディスプレイに接続されています。このエミュレートされた外部ディスプレイに接続するためのリファレンス計器クラスタ実装。

統合アーキテクチャ

統合コンポーネント

Instrument Cluster API の統合は、次の 3 つのコンポーネントで構成されます。

  • CarService
  • ナビゲーションアプリ
  • OEM インスツルメント クラスター サービス

統合コンポーネント

カーサービス

CarServiceナビゲーション アプリと車の間を仲介し、常に 1 つのナビゲーション アプリのみがアクティブになり、 android.car.permission.CAR_INSTRUMENT_CLUSTER_CONTROL権限を持つアプリのみが車にデータを送信できるようにします。

CarService 、すべての自動車固有のサービスをブートストラップし、一連のマネージャーを通じてこれらのサービスへのアクセスを提供します。サービスと対話するために、車内で実行されているアプリはこれらのマネージャーにアクセスできます。

インストルメント クラスター実装の場合、自動車 OEM は InstrumentClusterRendererService のカスタム実装を作成し、セカンダリ ディスプレイに接続されているClusterRenderingServiceサービスを更新する必要があります。

インストルメント クラスタをレンダリングするとき、ブート プロセス中に、 CarServiceセカンダリ ディスプレイに接続されているClusterRenderingServiceサービスのInstrumentClusterRendererServiceキーを読み取ります。 InstrumentClusterServiceの実装を見つけます。 AOSP では、このエントリは Navigation State API サンプル クラスター実装のレンダリング サービスを指します。

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

このエントリで参照されるサービスは初期化され、 CarServiceにバインドされます。 Google マップなどのナビゲーション アプリがCarInstrumentClusterManagerをリクエストすると、 CarServiceバインドされたInstrumentClusterRenderingServiceから計器クラスタの状態を更新するマネージャーを提供します。 (この場合、 bound はAndroid サービスを指します。)

インストルメントクラスターサービス

OEM は、セカンダリ ディスプレイに接続されるClusterRenderingServiceサービスのサブクラスを含む Android パッケージ (APK) を作成する必要があります。 ClusterRenderingServiceサービスはセカンダリ ディスプレイに接続されています。サンプル用です。

このクラスは次の 2 つの目的を果たします。

  • Android インターフェイスとインストゥルメント クラスター レンダリング デバイスを提供します (このページの目的)。
  • ターンバイターンのナビゲーション ガイダンスなど、ナビゲーション状態の更新を受信して​​レンダリングします。

最初の目的のために、 InstrumentClusterRendererServiceの OEM 実装は、車室内の画面に情報をレンダリングするために使用されるセカンダリ ディスプレイを初期化し、 InstrumentClusterRendererService.setClusterActivityOptions()メソッドとInstrumentClusterRendererService.setClusterActivityState()メソッドを呼び出してこの情報をCarServiceに伝達する必要があります。

2 番目の機能の場合、インストルメント クラスター サービスは、セカンダリ ディスプレイに接続されるClusterRenderingServiceサービスの実装を提供する必要があります。ナビゲーション ステータス更新イベントを受け取るインターフェイス。 eventTypeとしてエンコードされ、バンドル内にエンコードされたイベント データも含まれます。

統合シーケンス

次の図は、更新をレンダリングするナビゲーション状態の実装を示しています。

統合シーケンス

この図では、色は次のことを示しています。

  • 黄色。 Android プラットフォームによって提供されるCarServiceおよびCarNavigationStatusManager 。詳細については、 「自動車」「CAR_NAVIGATION_SERVICE」を参照してください。
  • シアン。 OEM によって実装されたInstrumentClusterRendererService
  • 紫。 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_NAVIGATIONappTypeパラメーターとして渡します。
  4. CarAppFocusManagerこのリクエストをCarServiceに伝えます。許可された場合、 CarServiceナビゲーション アプリ パッケージを検査し、カテゴリandroid.car.cluster.NAVIGATIONでマークされたアクティビティを見つけます。
  5. 見つかった場合、ナビゲーション アプリは、 InstrumentClusterRenderingServiceによって報告されたActivityOptionsを使用してアクティビティを起動し、インテントにエクストラとしてインストルメント クラスター表示プロパティを含めます。

APIを統合する

InstrumentClusterRenderingService実装は次のことを行う必要があります。

  • 次の値を AndroidManifest.xml に追加することで、シングルトン サービスとして指定されます。これは、初期化中やユーザーの切り替え中であっても、Instrument Cluster サービスの単一コピーが確実に実行されるようにするために必要です。
    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()中に、このサービスを使用してレンダリング ハードウェアとの通信を初期化します。オプションには次のものが含まれます。
    • インストルメント クラスターに使用するセカンダリ ディスプレイを決定します。
    • Instrument Cluster アプリがレンダリング イメージをレンダリングし、レンダリングされたイメージを外部ユニットに送信できるように、仮想ディスプレイを作成します (H.264 などのビデオ ストリーミング形式を使用)。
  4. 上記の表示の準備ができたら、このサービスはInstrumentClusterRenderingService#setClusterActivityLaunchOptions()を呼び出して、計測器クラスタ上でアクティビティを表示するために使用する必要がある正確なActivityOptionsを定義する必要があります。次のパラメータを使用します。
    • カテゴリー。 ClusterRenderingServiceサービスはセカンダリ ディスプレイに接続されています。
    • ActivityOptions.インストルメント クラスターでアクティビティを起動するために使用できるActivityOptionsインスタンス。たとえば、AOSP 上のサンプル インストルメント クラスタ実装から:
      getService().setClusterActivityLaunchOptions(
         CATEGORY_NAVIGATION,
         ActivityOptions.makeBasic()
            .setLaunchDisplayId(displayId));
      
  5. インストルメント クラスタがアクティビティを表示する準備ができたら、このサービスはInstrumentClusterRenderingService#setClusterActivityState()を呼び出す必要があります。次のパラメータを使用します。
    • category ClusterRenderingServiceサービスがセカンダリ ディスプレイに接続されています。
    • state ClusterRenderingServiceサービスで生成されたバンドルがセカンダリ ディスプレイに接続されています。
    • 必ず次のデータを提供してください。
      • 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 は次のことが可能になります。

  • クラスターに表示されているアクティビティを、フォーカスを保持しているナビゲーション アプリによって提供されるクラスター アクティビティに切り替えます。
  • フォーカスされたナビゲーション アプリにクラスター アクティビティがあるかどうかを検出できます。フォーカスされたナビゲーション アプリにクラスター アクティビティがない場合 (またはそのようなアクティビティが無効になっている場合)、OEM はこの信号を車の DIM に送信して、クラスターのナビゲーション ファセットが完全にスキップされるようにできます。

CarAppFocusManagerを使用して、アクティブ ナビゲーションや音声コマンドなどの現在のアプリ フォーカスを設定およびリッスンします。通常、システム内でアクティブに実行されている (または集中している) のは、このようなアプリの 1 つのインスタンスだけです。

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 をオンにします。
    1. 設定アプリで開発者モードを選択します。
    2. [設定] > [システム] > [詳細設定] > [開発者向けオプション] > [セカンダリ ディスプレイのシミュレート]に移動します。
  3. HU を再起動します。 ClusterRenderingServiceサービスはセカンダリ ディスプレイに接続されています。
  4. KitchenSink アプリを起動するには:
    1. 引き出しを開けます。
    2. インスタに行きます。集まる
    3. [メタデータの開始]をクリックします。

KitchenSink は NAVIGATION フォーカスを要求し、 DirectRenderingClusterサービスにインストルメント クラスター上にモックアップされたユーザー インターフェイスを表示するように指示します。