ラジオを実装する

このページでは、ハードウェア レベルとソフトウェア レベルでラジオを実装する方法について説明します。

システム コンポーネント

ブロードキャスト ラジオ スタックには次のコンポーネントが含まれます。

ブロードキャスト ラジオ アーキテクチャ
図 1. ブロードキャスト ラジオ アーキテクチャ

ラジオ リファレンス アプリ

ラジオの制御を実装する方法について詳しくは、ラジオの制御の実装をご覧ください。

Java ラジオアプリのサンプル(packages/apps/Car/Radio)はリファレンス実装として機能します。アプリサービスは、開始時にラジオ マネージャーにラジオ チューナーを開くようリクエストします。これにより、アプリはラジオ チューナーにリクエストを送信して、特定のラジオ放送局や周波数への調整、次に利用可能なラジオ放送局のシークなどを行うことができます。アプリは、ラジオのラジオ マネージャーとラジオ チューナーから、現在の番組情報、ラジオ番組リスト、構成、ベンダー定義パラメータなどに関する更新情報を受け取ります。リファレンス ラジオ アプリは AM ラジオと FM ラジオのみに対応しています。OEM は、必要に応じてラジオアプリを変更または交換できます。

ラジオ マネージャー

アプリがラジオ マネージャーにチューナーを開くようにリクエストすると、ラジオ マネージャー(frameworks/base/core/java/android/hardware/radio/RadioManager.java)はブロードキャスト ラジオ サービスにチューナー セッションを開くようにリクエストします。そして、そのセッションはラジオ チューナー(frameworks/base/core/java/android/hardware/radio/RadioTuner.java)にラップされ、アプリに返されます。ラジオ チューナーは、ラジオアプリから呼び出すことができる API(選局、ステップ、キャンセルなど)を定義し、ブロードキャスト ラジオ サービスにリクエストを送信します。ラジオ チューナーで定義されたコールバック メソッド(RadioTuner.Callback)は、ブロードキャスト ラジオ HAL に関する更新情報(現在の番組情報、番組リスト、ベンダー定義パラメータなど)をブロードキャスト ラジオ サービスからアプリに送信します。

ブロードキャスト ラジオ サービス

ブロードキャスト ラジオ サービス(frameworks/base/services/core/java/com/android/server/broadcastradio)は、ブロードキャスト ラジオ HAL のクライアント サービスです。ブロードキャスト ラジオ サービスは、複数のラジオ マネージャーとブロードキャスト ラジオ HAL との調整を行います。ブロードキャスト ラジオ サービスは、HAL インターフェース定義言語(HIDL)Android インターフェース定義言語(AIDL)のブロードキャスト ラジオ HAL をサポートしています。ブロードキャスト ラジオ サービスは、AIDL HAL サービスが存在する場合は AIDL HAL にリンクします。存在しない場合は、HIDL HAL にリンクします。ブロードキャスト ラジオ サービスは、利用可能な HAL インスタンス(AM、FM、DAB など)ごとにラジオ モジュールを作成します。

各ラジオ マネージャーは、ラジオのタイプに基づいて、対応するラジオ モジュールにチューナー セッションを作成するようブロードキャスト ラジオ サービスにリクエストできます。各チューナー セッションは、選局、ステップ、キャンセルなどのメソッド(HAL インターフェース内で定義)を呼び出して、対応するブロードキャスト ラジオ HAL インスタンスに対するオペレーションを実行できます。1 つのチューナー セッションが HAL 更新情報(現在の番組情報、番組リスト、構成フラグ、ベンダー パラメータなど)に関する HAL インスタンスからコールバックを受信すると、更新情報に関するコールバックが同じラジオ モジュールにリンクされたすべてのラジオ チューナーに送信されます。

ブロードキャスト ラジオ HAL

ブロードキャスト ラジオの HIDL インターフェースと AIDL インターフェースの詳細、およびそれらの違いについて詳しくは、ブロードキャスト ラジオ HAL インターフェースをご覧ください。

ブロードキャスト ラジオ Hardware Abstraction Layer

以降のセクションでは、ハードウェア レイヤと連携してブロードキャスト ラジオを実装する方法について説明します。

ブロードキャスト ラジオ HAL インターフェース

ブロードキャスト ラジオ HAL は、ハードウェア レベルでデータ構造とインターフェースを提供し、AM / FM ラジオや DAB ラジオなどのブロードキャスト ラジオを実装します。

HIDL 2.0 インターフェースと AIDL インターフェース

ブロードキャスト ラジオ HAL では、以降のセクションで説明するインターフェースを使用します。

通知リスナー

IAnnouncementListener は、通知リスナーのためのコールバック インターフェースです。ブロードキャスト ラジオ HAL に登録して通知を受信できます。インターフェースには、次のメソッドがあります。

IAnnouncementListener
説明: 通知リストが変更されるたびに呼び出されます。
HIDL 2.0 oneway onListUpdated(vec<Announcement> announcements)
AIDL oneway void onListUpdated(in Announcement[] announcements)
ハンドルのクローズ

ICloseHandle は、アクティブなインターフェースを必要としないコールバックを削除するための汎用クローズ ハンドルです。

ICloseHandle
説明: ハンドルをクローズします。
HIDL 2.0 close()
AIDL void close()

コールバック インターフェース

ITunerCallback は、HAL クライアント サービスに更新情報を送信するためにブロードキャスト ラジオ HAL によって呼び出されるコールバック インターフェースです。

ITunerCallback
説明: 選局オペレーション(AIDL での選局、シーク、または HIDL でのスキャンおよびステップ成功時)が非同期で失敗したときに、HAL によって呼び出されます。
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
説明: 選局、AIDL でのシーク、HIDL でのスキャン、またはステップの成功時に呼び出されます。
HIDL 2.0 oneway onTuneFailed(Result result, ProgramSelector selector)
AIDL void onTuneFailed(in Result result, in ProgramSelector selector)
説明: 選局、AIDL でのシーク、HIDL でのスキャン、またはステップの成功時に呼び出されます。
HIDL 2.0 oneway onCurrentProgramInfoChanged(ProgramInfo info)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
説明: 番組リストが更新されたときに呼び出されます。各チャンクのサイズは 500 KiB に制限する必要があります。
HIDL 2.0 oneway onProgramListUpdated(ProgramListChunk chunk)
AIDL oneway onProgramListUpdated(ProgramListChunk chunk)
説明: アンテナが接続または切断されたときに呼び出されます。
HIDL 2.0 oneway onAntennaStateChange(bool connected)
AIDL void onCurrentProgramInfoChanged(in ProgramInfo info)
説明: ベンダー固有のパラメータ値が HAL 内で更新されたときに呼び出されます(HAL クライアントで setParameters を呼び出した後に呼び出すことはできません)。
HIDL 2.0 oneway onParametersUpdated(vec<VendorKeyValue> parameters)
AIDL void onParametersUpdated(in VendorKeyValue[] parameters)
説明: AIDL の新機能です。設定フラグが HAL 内で更新されたときに呼び出されます(HAL クライアントによって setConfigFlag を呼び出した後に呼び出すことはできません)。
HIDL 2.0 該当はありません。
AIDL void onConfigFlagUpdated(in ConfigFlag flag, in boolean value)

プライマリ ブロードキャスト ラジオ HAL インターフェース

IBroadcastRadio は、ブロードキャスト ラジオ HAL のプライマリ インターフェースです。HIDL 2.0 HAL で、チューナーへの ITunerSession インターフェースを使用してオペレーションを呼び出します。ただし、一度に有効にできるチューナーは 1 つだけです(各ブロードキャスト ラジオ HAL インスタンスにチューナー チップが 1 つしかない場合)。 ITunerSession が AIDL インターフェースから削除され、そのインターフェースが IBroadcastRadio に移動しました。

IBroadcastRadio
説明: モジュールとその機能の説明を取得します。
HIDL 2.0 getProperties() generates (Properties properties)
AIDL Properties getProperties()
説明: 現在のまたは可能な AM / FM リージョン構成を取得します。
HIDL 2.0 getAmFmRegionConfig(bool full) generates (Result result, AmFmRegionConfig config)
AIDL AmFmRegionConfig getAmFmRegionConfig(bool full)
説明: 現在の DAB リージョン構成を取得します。
HIDL 2.0 getDabRegionConfig() generates (Result result, vec<DabTableEntry> config)
AIDL DabTableEntry[] getDabRegionConfig()
説明: ラジオ モジュールのキャッシュから画像を取得します。AIDL では、バインダ トランザクション バッファのハードリミットにより、画像サイズが 1 MB 未満である必要があります。
HIDL 2.0 getImage(uint32_t id) generates (vec<uint8_t> image)
AIDL byte[] getImage(in int id)
説明: 通知リスナーを登録します。
HIDL 2.0 registerAnnouncementListener(vec<AnnouncementType> enabled,IAnnouncementListener listener) generates (Result result, ICloseHandle closeHandle)
AIDL ICloseHandle registerAnnouncementListener(in IAnnouncementListener listener, in AnnouncementType[] enabled)
説明:
  • HIDL HAL: 新しいチューナー セッションを開始したときに、古いセッションを終了する必要があります。
  • AIDL HAL: 利用できるチューナー セッションがないため、チューナー コールバックのみを設定する必要があります。 セッションがある場合は、古いコールバックの設定を解除する必要があります。
HIDL 2.0 openSession(ITunerCallback callback) により生成 (Result result, ITunerSession session)
AIDL void setTunerCallback(in ITunerCallback callback)
説明:
  • HIDL HAL: チューナー セッションの終了は失敗してはならず、1 回のみ行う必要があります。
  • AIDL HAL: チューナーはなく、チューナー コールバックの設定のみを解除する必要があります。
HIDL 2.0 close()
AIDL unsetTunerCallback()
説明: 指定された番組に選局します。
HIDL 2.0 tune(ProgramSelector program) generates (Result result)
AIDL void tune(in ProgramSelector program)
説明: 電波を通して、次の有効な番組を検索します。AIDL での混乱を避けるため、scanseek に変更されています。
HIDL 2.0 scan(bool directionUp, bool skipSubChannel) generates (Result result)
AIDL void seek(in boolean directionUp, in boolean skipSubChannel)
説明: どの番組にも占有されていない隣接チャンネルにステップします。
HIDL 2.0 step(bool directionUp) generates (Result result)
AIDL void step(in boolean directionUp)
説明: 保留中の選局、HIDL でのスキャン、AIDL でのシーク、またはステップ オペレーションをキャンセルします。
HIDL 2.0 cancel()
AIDL void cancel()
説明: 番組リストにフィルタを適用し、onProgramListUpdated コールバックを介して番組リストの更新情報の送信を開始します。
HIDL 2.0 startProgramListUpdates(ProgramFilter filter) generates (Result result)
AIDL void startProgramListUpdates(in ProgramFilter filter)
説明: 番組リストの更新情報の送信を停止します。
HIDL 2.0 stopProgramListUpdates()
AIDL void stopProgramListUpdates()
説明: 特定の構成フラグの現在の設定を取得します。
HIDL 2.0 isConfigFlagSet(ConfigFlag flag) generates (Result result, bool value)
AIDL boolean isConfigFlagSet(in ConfigFlag flag)
説明: 特定の構成フラグを設定します。
HIDL 2.0 setConfigFlag(ConfigFlag flag, bool value) generates (Result result)
AIDL void setConfigFlag(in ConfigFlag flag, boolean value)
説明: ベンダー固有のパラメータ値を設定します。
HIDL 2.0 setParameters(vec<VendorKeyValue> parameters)

により生成

(vec<VendorKeyValue> results)
AIDL VendorKeyValue[] setParameters(in VendorKeyValue[] parameters)
説明: ベンダー固有のパラメータ値を取得します。
HIDL 2.0 getParameters(vec<string> keys) generates (vec<VendorKeyValue> parameters)
AIDL VendorKeyValue[] getParameters(in String[] keys)

インターフェースの説明

非同期動作

各選局オペレーション(選局、HIDL でのスキャン、AIDL でのシーク、ステップ)は時間がかかる可能性があり、スレッドが長時間ブロックされないようにするため、時間のかかるオペレーションを後で実行してステータスや結果をすばやく返すように、スケジュールされます。具体的には、各オペレーションで以下を行う必要があります。

  • 保留中のすべての選局オペレーションをキャンセルします。
  • メソッド入力とチューナーのステータスに基づいてオペレーションが処理できるかどうかをチェックします。
  • 選局タスクをスケジュールしたら、すぐに HIDL での Result、または AIDL での status を返します。 Result または statusOK の場合、タイムアウトなどが理由で選局タスクが失敗または完了したときには、チューナー コールバック tuneFailed または currentProgramInfoChanged を呼び出す必要があります。

同様に startProgramListUpdates は、番組リストの更新という時間のかかるタスクを後で実行して、ステータスまたは結果をすばやく返すようにスケジュールします。このメソッドでは、まず保留中の更新リクエストをキャンセルしてから、更新タスクをスケジュールし、結果をすばやく返します。

競合状態

選局オペレーション(選局、HIDL でのスキャン、AIDL でのシーク、ステップ)の非同期動作のため、オペレーションのキャンセルと選局オペレーションの間に競合状態が生じます。HAL が選局オペレーションを完了してコールバックが完了する前に cancel が呼び出された場合、キャンセルを無視して、コールバックが完了して HAL クライアントによって受信されるようにする必要があります。

同様に、HAL が番組リストの更新を完了し、onCurrentProgramInfoChanged コールバックが完了する前に stopProgramListUpdates が呼び出された場合、stopProgramListUpdates を無視し、コールバックが完了されるようにする必要があります。

データサイズの上限

バインダ トランザクション バッファにはハードリミットがあるため、AIDL HAL では、インターフェース メソッドで渡されるデータサイズが大きい場合について、明確なデータ制限が定められています。

  • getImage では、返される画像が 1 MB 未満である必要があります。
  • onProgramListUpdate では、各 chunk が 500 KiB 未満である必要があります。 これよりも大きい番組リストは、HAL 実装によって複数のチャンクに分割され、複数のコールバックを介して送信されなければなりません。

AIDL HAL データ構造の変更

AIDL を利用するブロードキャスト ラジオ AIDL HAL で定義されたデータ構造には、インターフェースの変更に加え、次の変更が適用されています。

  • Constant 列挙型が AIDL では削除され、IBroadcastRadio で定数 int として定義されました。同時に、ANTENNA_DISCONNECTED_TIMEOUT_MS の名前が ANTENNA_STATE_CHANGE_TIMEOUT_MS に変更されました。新しい定数 int TUNER_TIMEOUT_MS が追加されました。選局、シーク、ステップの各オペレーションはすべて、この時間内に完了する必要があります。
  • 列挙型 RDSDeemphasis が AIDL で削除され、AmFmRegionConfig で定数 int として定義されました。それに応じて、ProgramInfofmDeemphasisfmRds はどちらも、それぞれのフラグのビット演算結果である int として宣言されます。同時に、D50D75 の名前がそれぞれ DEEMPHASIS_D50DEEMPHASIS_D75 に変更されました。
  • 列挙型 ProgramInfoFlags が AIDL で削除され、ProgramInfo で int として定義されました。また、接頭辞 FLAG_ が追加されました。これに応じて、ProgramInfoinfoFlags が、フラグのビット演算結果である int として宣言されます。また、TUNED の名前も FLAG_TUNABLE に変更され、その定義、つまりラジオ局を選局できる範囲の説明がわかりやすくなりました。
  • AIDL で scan の名前が seek に変更されたため、AmFmBandRangescanSpacing の名前が seekSpacing に変更されました。
  • union のコンセプトが AIDL で導入されたため、HIDL HAL で定義された MetadataKeyMetadata は使用されなくなりました。AIDL union Metadata は、AIDL HAL で定義されます。以前は MetadataKey であった各列挙値は、定義に応じて、文字列型または int 型の Metadata フィールドになります。

ラジオの制御の実装

ラジオの制御は、MediaSessionMediaBrowse により実装します。これらによって、メディアアプリと音声アシスタント アプリでラジオを制御できます。詳しくは、developer.android.com の自動車向けメディアアプリを作成するをご覧ください。

メディア ブラウズ ツリーの実装は、packages/apps/Car/libs の car-broadcastradio-support ライブラリで提供されています。このライブラリでは、URI との相互変換を行うための ProgramSelector の拡張機能も提供されています。ラジオの実装でこのライブラリを使用して、関連するブラウズツリーを作成することをおすすめします。

メディア ソース スイッチャー

ラジオと、メディアに表示される他のアプリとの間でシームレスな移行を実現するため、car-media-common ライブラリには、ラジオアプリに統合する必要のあるクラスが含まれています。MediaAppSelectorWidget は、ラジオアプリの XML に含めることができます(参照のメディアアプリやラジオアプリで使用するアイコンとプルダウン)。

<com.android.car.media.common.MediaAppSelectorWidget
     android:id="@+id/app_switch_container"
     android:layout_width="@dimen/app_switch_widget_width"
     android:layout_height="wrap_content"
     android:background="@drawable/app_item_background"
     android:gravity="center" />

このウィジェットで AppSelectionFragment を起動し、切り替え可能なメディアソースのリストを表示します。提供されたもの以外の UI が必要な場合は、スイッチャーを表示する際に AppSelectionFragment を起動するカスタム ウィジェットを作成できます。

AppSelectionFragment newFragment = AppSelectionFragment.create(widget,
            packageName, fullScreen);
    newFragment.show(mActivity.getSupportFragmentManager(), null);

実装例については、packages/apps/Car/Radio にある参照用ラジオアプリの実装をご覧ください。

詳細な制御の仕様

MediaSessionMediaSession.Callback 経由)インターフェースは、現在再生中のラジオ番組に対して次のような制御メカニズムを提供します。

  • onPlayonStop。ラジオの再生をミュート(またはミュートを解除)します。
  • onPause。タイムシフトが有効な一時停止(サポートされる場合)。
  • onPlayFromMediaId。最上位フォルダのコンテンツをすべて再生します。例:「FM を再生」や「ラジオを再生」。
  • onPlayFromUri。特定の周波数を再生します。例:「88.5 FM を再生」。
  • onSkipToNextonSkipToPrevious。次または前のラジオ局を選局します。
  • onSetRating。お気に入りに追加またはお気に入りから削除します。

MediaBrowser は、3 つのタイプの最上位のディレクトリに対して選局可能な MediaItem を公開します。

  • (省略可番組(ラジオ局)。このモードは通常、ダブルチューナー ラジオで、ユーザーの現在地で利用可能かつ選局可能なすべてのラジオ局を示すために使用されます。
  • お気に入り。お気に入りリストに追加されるラジオ番組。一部で利用できない場合があります(受信範囲外)。
  • 周波数帯チャンネル。現在の地域で受信可能なすべてのチャンネル(87.9、88.1、88.3、88.5、88.7、88.9、89.1 など)。すべての周波数帯が、最上位のディレクトリを個別に持ちます。
MediaBrowserService のツリー構造
Figure 2. MediaBrowserService のツリー構造

これらの各フォルダ(AM / FM / 番組)の各要素は、MediaSession で使用して選局できる URI を含む MediaItem です。各最上位フォルダ(AM / FM / 番組)は、MediaSession で使用して再生をトリガーできる mediaId を含む MediaItem で、これらは OEM が決定できます。たとえば、「FM を再生」、「AM を再生」、「ラジオを再生」はすべて、mediaId を使用して OEM ラジオアプリに送信する非指定のラジオクエリです。一般的なリクエストと mediaId から何を再生するかはラジオアプリが決定します。

MediaSession

ブロードキャスト ストリームを一時停止するという概念はないため、再生、一時停止、停止の操作が必ずしもラジオに適用されるとは限りません。ラジオでは、停止の操作はストリームのミュートに関連付けられており、再生はミュートの解除に関連付けられています。

一部のラジオ チューナー(またはアプリ)は、コンテンツをキャッシュに保存し、後で再生することで、ブロードキャスト ストリームの一時停止をシミュレートできます。このような場合には、onPause を使用します。

mediaId および URI アクションから再生すると、MediaBrowser インターフェースから取得したラジオ局が選局されます。mediaId は、ラジオアプリが提供する任意の文字列で、一意(各 ID が 1 つのアイテムのみを参照)かつ不変的(各アイテムがセッション全体で同じ ID を維持)な値を指定します。この値により、特定のラジオ局を識別できるようになります。この URI は、明確に定義されたスキーマです。つまり、ProgramSelector が URI 化された形式をとります。これにより uniquity 属性は保持されますが、不変である必要はなく、ラジオ局の周波数が変化した場合、属性が変わる場合があります。

設計により、onPlayFromSearch は使用されません。MediaBrowser ツリーからの検索結果の選択については、クライアント側(コンパニオン アプリ)で行います。検索結果をラジオアプリ側から選択する場合、複雑さが増して、文字列クエリの形式に関する正式な契約が必要になり、さまざまなハードウェア プラットフォームで同様のユーザー エクスペリエンスを達成できなくなる可能性があります。

注: ラジオアプリには、MediaBrowser インターフェースを介してクライアントに公開されないラジオ局名の検索に役立つ追加情報は含まれていません。

前後のラジオ局へのスキップについては、そのときの状況によって処理が異なります。

  • お気に入りリストからラジオ局を選局するアプリの場合、そのアプリでお気に入りリストから次のラジオ局に移動できます。
  • 番組リストからラジオ局を選局した場合、チャンネル番号で並べ替えられ、次に受信可能なラジオ局に移動する場合があります。
  • 任意のラジオ局を選局した場合、放送信号がない場合でも、次の数値上のラジオ局が選局される場合があります。

これらのアクションはラジオアプリによって処理されます。

エラー処理

TransportControls アクション(再生、停止、次へ)では、アクションが成功したかどうかに関するフィードバックは提供されません。エラーを表示する唯一の方法は、MediaSession の状態がエラー メッセージ付きの STATE_ERROR になるように設定することです。

これらのアクションの処理はラジオアプリ側で行い、アクションを実行するかエラーの状態を設定する必要があります。再生コマンドがすぐに実行されない場合は、コマンドの実行中に再生状態を STATE_CONNECTING(直接選局している場合)、 STATE_SKIPPING_TO_PREVIOUS または NEXT に変更します。

クライアントは PlaybackState を監視して、セッションによって現在の番組がリクエストされたものになったか、あるいはエラー状態になったかを確認します。STATE_CONNECTING は 30 秒を超えないようにする必要があります。ただし、特定の AM / FM 周波数を直接選局すると、すばやく選局が実行されます。

お気に入りへの追加と削除

MediaSession は評価機能をサポートしており、お気に入りを整理するために使用できます。タイプ onSetRating の評価で呼び出された RATING_HEART は、現在選局されているラジオ局をお気に入りリストから追加または削除します。

このモデルは、保存済みのそれぞれのお気に入りが数値スロット(通常は 1~6)に割り当てられたレガシー プリセットではなく、順序のない無制限のお気に入りリストを想定しています。そのため、プリセットに基づくシステムは onSetRating オペレーションと互換性がありません。

MediaSession API の制限により、現在選局されているステーションのみを追加または削除できます。たとえば、アイテムを削除する前に、まずアイテムを選択する必要があります。これは、コンパニオン アプリなどの MediaBrowser クライアントの制限です。ラジオアプリが同様の制限を受けることはありません。アプリがお気に入りをサポートしていない場合、この部分は省略可能です。

MediaBrowser

特定の地域で有効な周波数、つまり数値上のチャンネルを表すため(任意に選局できるデバイスを備えている場合)、ラジオ局が存在し得るすべてのチャンネル(周波数)を周波数帯ごとにリストします。米国地域では、87.8~108.0 MHz の範囲(0.2 MHz の周波数間隔)の 101 の FM チャンネルと、530~1700 kHz の範囲(10kHz の周波数間隔)の 117 の AM チャンネルとなります。HD ラジオは、同じチャンネル スペースを使用するため、FM や AM ごとの区別はありません。

現在利用可能なラジオ番組のリストでは、これはダイレクト オーディオ ブロードキャスト(DAB)アンサンブルによるグループ化などのスキームを表示できないという点でフラットなものとなっています。

お気に入りリストのエントリに選局できない場合があります。たとえば、特定の番組が受信圏外の場合などです。エントリを受信できるかどうかについて、ラジオアプリで事前に検出できることもあれば、できないこともあります。検出できる場合、受信圏外のエントリについては、ラジオアプリで再生可能として表示しないこともできます。

最上位フォルダの特定には、Bluetooth と同じメカニズムが適用されます。つまり、 MediaDescription オブジェクトの Extras バンドルには、Bluetooth が EXTRA_BT_FOLDER_TYPE で処理する、チューナー固有のフィールドが含まれます。ラジオ放送の場合、これにより、公開 API で次の新しいフィールドを定義します。

  • EXTRA_BCRADIO_FOLDER_TYPE = "android.media.extra.EXTRA_BCRADIO_FOLDER_TYPE"。次のいずれかの値となります。
    • BCRADIO_FOLDER_TYPE_PROGRAMS = 1。現在利用可能な番組。
    • BCRADIO_FOLDER_TYPE_FAVORITES = 2。お気に入り。
    • BCRADIO_FOLDER_TYPE_BAND = 3。特定の周波数帯に存在する、すべての数値上のチャンネル。

    すべての関連データが既存の MediaBrowser.MediaItem スキームに適合するため、ラジオ固有のカスタム メタデータ フィールドを定義する必要はありません。

    • 番組名(RDS PS、DAB サービス名)。MediaDescription.getTitle
    • FM 周波数。URI(ProgramSelector を参照)または MediaDescription.getTitle(エントリが BROADCASTRADIO_FOLDER_TYPE_BAND フォルダにある場合)。
    • ラジオ固有の識別子(RDS PI、DAB SId)。 MediaDescription.getMediaUri は ProgramSelector で解析。

    通常は、現在の番組またはお気に入りリストのエントリの FM 周波数を取得する必要はありません(クライアントはメディア ID を利用するため)。ただし、そのようなニーズが発生した場合(表示の目的でなど)、周波数は URI に含まれており、ProgramSelector で解析できます。ただし、現在のセッション内のアイテムを選択するために URI を使用することはおすすめしません。詳細については、ProgramSelector をご覧ください。

    パフォーマンスやバインダ関連の問題を回避するために、MediaBrowser サービスでページネーションがサポートされている必要があります。

    注: デフォルトでは、ページ分割は、オプション処理なしで onLoadChildren() バリアント内に実装されます。

    すべての種類のリスト(未加工のチャンネル、検出された番組、お気に入り)からの関連エントリは、異なる mediaId が含まれる可能性があります(ラジオアプリによって異なります。つまり、サポート ライブラリに応じてそれらのリストは異なります)。URI(ProgramSelector 形式)は、ほとんどの場合において、未加工のチャンネルと検出された番組とで異なります(RDS のない FM の場合を除く)。ただし、検出された番組とお気に入りの番組間はほとんど同じです(AF が更新された場合などを除く)。

    さまざまな種類のリストのエントリに異なる mediaId を持たせることで、それらに応じて多様なアクションを実行できます。最近選択した MediaItem のフォルダに応じて、onSkipToNext でお気に入りリストまたはすべての番組リストをトラバースできます(MediaSession を参照)。

    特別な選局アクション

    番組リストでは、ユーザーは特定のラジオ局を選局することはできますが、「FM を選局する」などの大まかなリクエストを行うことはできません。このようなリクエストを出しても、最近聴いた FM のラジオ局が選局される可能性があります。

    このようなアクションをサポートするために、一部の最上位ディレクトリには、(フォルダの FLAG_BROWSABLE とともに)FLAG_PLAYABLE フラグが設定されています。

    アクション 選局先 発行方法
    ラジオの再生 任意のラジオ チャンネル startService(ACTION_PLAY_BROADCASTRADIO)

    または

    playFromMediaId(MediaBrowser.getRoot())
    FM の再生 FM チャンネルのいずれか FM 周波数帯の mediaId から再生

    どの番組を選局するかは、アプリが決定します。通常は、指定されたリストにある最近選局したチャンネルです。ACTION_PLAY_BROADCASTRADIO の詳細については、一般的な再生インテントをご覧ください。

    接続の検出およびサービスへの接続

    PackageManager は、ラジオ放送ツリーを提供する MediaBrowserService を直接検出できます。そのためには、ACTION_PLAY_BROADCASTRADIO インテント(一般的な再生インテントを参照)と MATCH_SYSTEM_ONLY フラグで resolveService を呼び出します。ラジオ放送を提供するすべてのサービス(AM/FM や衛星ラジオなど、複数ある場合があります)を検索するには、queryIntentServices を使用します。

    解決されたサービスも android.media.browse.MediaBrowserService バインド インテントを処理します。これは GTS で検証されます。

    選択した MediaBrowserService に接続するには、特定のサービス コンポーネントと connectMediaBrowser インスタンスを作成します。接続の確立後、getSessionToken を介して MediaSession へのハンドルを取得できます。

    ラジオアプリは、サービスの onGetRoot の実装で接続可能なクライアント パッケージを制限できます。ホワイトリストへの登録なしでシステムアプリが接続できるように、アプリ側で設定します。ホワイトリストへの登録の詳細については、アシスタント アプリ パッケージと署名を受け入れる場合をご覧ください。

    このようなソースサポートのないデバイスに、固有のソースを持つアプリ(ラジオアプリなど)がインストールされた場合、アプリは ACTION_PLAY_BROADCASTRADIO インテントを処理するのと同時にアプリ自体を引き続きアドバタイズしますが、その MediaBrowser ツリーにはラジオ固有のタグが含まれません。したがって、クライアントは次の方法で特定のソースがデバイスで利用できるかどうかを確認できます。

    1. ラジオサービスを検出します(ACTION_PLAY_BROADCASTRADIOresolveService を呼び出します)。
    2. MediaBrowser を作成して接続します。
    3. EXTRA_BCRADIO_FOLDER_TYPE を追加して、MediaItem の有無を判断します。

    注: 多くの場合、クライアントは利用可能なすべての MediaBrowser ツリーをスキャンして、特定のデバイスで利用可能なソースをすべて検出する必要があります。

    周波数帯の名前

    「周波数帯リスト」は、フォルダ タイプ タグが BCRADIO_FOLDER_TYPE_BAND に設定された最上位ディレクトリのセットとして表されます。MediaItem のタイトルはそれぞれ、周波数帯の名前を表すローカライズされた文字列です。ほとんどの場合、英語の翻訳と同様ですが、クライアントはその前提に依存できません。

    特定の周波数帯を検索するための安定したメカニズムとして、周波数帯フォルダには別のタグ EXTRA_BCRADIO_BAND_NAME_EN が追加されます。これは、ローカライズされていない周波数帯名で、次の定義済みの値のうちいずれか 1 つだけを取得できます。

    • AM
    • FM
    • DAB

    このリストにない周波数帯には、周波数帯名のタグを設定できません。リストにある周波数帯については、タグを設定する必要があります。HD ラジオは AM / FM と同じ基盤となるメディアを使用するため、個別の周波数帯が列挙されることはありません。

    一般的な再生インテント

    特定のソース(ラジオや CD など)を再生する専用の各アプリは、非アクティブな状態(ブート後など)などからコンテンツの再生を開始するための、一般的な再生インテントを処理する必要があります。再生するコンテンツの選択方法はアプリが決定しますが、通常は、最近再生したラジオ番組または CD トラックが選択されます。オーディオ ソースごとに定義された異なるインテントがあります。

    • android.car.intent.action.PLAY_BROADCASTRADIO
    • android.car.intent.action.PLAY_AUDIOCD: CD-DA または CD テキスト
    • android.car.intent.action.PLAY_DATADISC: CD/DVD など、データ保存が可能な光ディスク(CD-DA は除く)。ミックスモード CD の場合もあります
    • android.car.intent.action.PLAY_AUX: AUX ポートの指定なし
    • android.car.intent.action.PLAY_BLUETOOTH
    • android.car.intent.action.PLAY_USB: USB デバイスの指定なし
    • android.car.intent.action.PLAY_LOCAL: ローカル メディア ストレージ(内臓フラッシュ)

    一般的な再生コマンドそのものとサービス ディスカバリの 2 つの問題が同時に解決されるため、一般的な再生コマンドにはインテントが使用されます。このようなインテントを使用するその他のメリットは、MediaBrowser セッションを開かずに単純なアクションを実行できることです。

    これらのインテントによって解決されるより重要な問題は、サービス ディスカバリに関連する問題です。インテントを使用することで、サービス ディスカバリの手順が簡単かつ明確になります(接続の検出およびサービスへの接続を参照)。

    クライアントの実装を簡単にするため、再生コマンド(ラジオアプリでも実装する必要があります)を発行する代わりに、ルートノードの rootId(mediaId として使用)で playFromMediaId を発行することもできます。ルートノードは再生可能なものではありませんが、rootId は mediaId として消費可能にできる任意の文字列です。ただし、クライアントがこの仕組みを理解する必要はありません。

    ProgramSelector

    mediaIdMediaBrowserService からチャンネルを選択するには十分ですが、セッションにバインドされており、プロバイダ間での一貫性はありません。場合によっては、セッションとデバイス間で一貫性を持たせるために、クライアント側に絶対ポイント(絶対周波数など)が必要となる場合があります。

    デジタルラジオ放送の時代では、特定のラジオ局を選局するためには周波数だけでは不十分です。そのため、ProgramSelector を使用してアナログ チャンネルまたはデジタル チャンネルを選局します。ProgramSelector は次の 2 つのパートで構成されます。

    • プライマリ ID。特定のラジオ局を示す固有で不変の識別子。変更されることはありませんが、ラジオ局を選局するためには、この ID だけでは不十分な場合があります。例: RDS PI コード(米国ではコールサインに変換されることがあります)。
    • セカンダリ ID。ラジオ局の選局に役立つ追加の識別子(周波数など)。他のラジオ受信ハードウェアが提供する識別子を含む場合もあります。たとえば、アナログ放送では DAB ラジオ局のフォールバックが起きることがあります。

    MediaBrowser ベースまたは MediaSession ベースのソリューションに ProgramSelector を適合させるには、URI スキーマを定義してシリアル化します。スキーマは次のように定義します。

    broadcastradio://program/<primary ID type>/<primary ID>?
    <secondary ID type>=<secondary ID>&<secondary ID type>=<secondary ID>

    この例では、セカンダリ ID の部分(? という疑問符の後に表示)はオプションです。不変の識別子を mediaId として使用する際は省略できます。次に例を示します。

    • broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=88500&AMFM_FREQUENCY=103300
    • broadcastradio://program/AMFM_FREQUENCY/102100
    • broadcastradio://program/DAB_SID_EXT/14895264?RDS_PI=1234

    program の権限部分(AKA ホスト)には、スキームを将来拡張のための余地が確保されています。識別子タイプの文字列は、IdentifierType の HAL 2.x 定義で名前として正確に指定され、値の形式は 10 進数または 16 進数(プレフィックスは 0x)です。

    ベンダー固有の識別子はすべて、VENDOR_ プレフィックスで表されます。たとえば、VENDOR_0VENDOR_START に対応し、VENDOR_1VENDOR_START プラス 1 に対応します。こうした URI は、生成元のラジオ受信ハードウェアに固有のものであり、別の OEM で作成されたデバイス間では転送できません。

    これらの URI は、最上位のラジオフォルダの下の各 MediaItem に割り当てる必要があります。また、MediaSession は playFromMediaIdplayFromUri の両方をサポートする必要があります。ただし URI は、ラジオのメタデータ(FM 周波数など)を抽出して永続的に保管することが主な使い道になります。URI ですべてのメディア アイテムを使用できる保証はありません(例: プライマリ ID タイプがフレームワークでまだサポートされていない場合)。一方、メディア ID は常に機能します。URI を使用してクライアントが現在の MediaBrowser セッションからアイテムを選択することは推奨されません。代わりに、playFromMediaId を使用してください。ただし、サービスを提供するアプリの場合は例外で、正当な理由があれば欠落した URI が予約されます。

    初期の設計では、:// シーケンスの代わりに単一のコロンをスキーム部分の後に使用しています。ただし、前者は android.net.Uri では絶対階層の URI 参照をサポートしていません。

    他のソースタイプ

    他のオーディオ ソースも同様に処理されます。たとえば補助入力(AUX 入力)やオーディオ CD プレーヤーなどです。

    1 つのアプリが複数のタイプのソースを提供することもあります。そのような場合、ソースの種類ごとに個別の MediaBrowserService を作成することをおすすめします。また、複数のソースまたは MediaBrowserService の提供に合わせて設定を行っても、単一のアプリ内には単一の MediaSession を持たせることを強くおすすめします

    オーディオ CD

    オーディオ CD と同様に、オーディオ CD を処理するアプリは、特定の CD のトラックすべてを含む単一(CD チェンジャーを備えたシステムでは複数)のブラウズ可能なエントリを持つ MediaBrowser を公開します。システムがすべての CD のトラックに関する情報を持たない場合(たとえば、すべてのディスクが一度にカートリッジに挿入され、すべての読み取りが完了していない場合)、ディスク全体の MediaItem は BROWSABLE プラス PLAYABLE ではなく、単に PLAYABLE となります。特定のスロットにディスクが挿入されていない場合、アイテムは PLAYABLE にも BROWSABLE にもなりませんが、各スロットは常にツリー内に表示されていなければなりません。

    オーディオ CD のツリー構造
    図 3. オーディオ CD のツリー構造

    これらのエントリはラジオ放送のフォルダと同様の方法でマークされ、MediaDescription API で定義された次のような追加フィールドを含みます。

    • EXTRA_CD_TRACK: オーディオ CD の MediaItem ごとに、1 から始まるトラック番号。
    • EXTRA_CD_DISK: 1 から始まるディスク番号。

    CD-Text 対応システムおよび互換ディスクの場合、最上位の MediaItem にはディスクのタイトルが含まれます。同様に、トラックの MediaItem にはトラックのタイトルが含まれます。

    補助入力(AUX 入力)

    補助入力(AUX 入力)を処理するアプリは、ポート内の AUX を表す単一のエントリ(複数のポートが存在する場合は 1 つ以上)の MediaBrowser ツリーを公開します。それぞれの MediaSession は mediaId を取得し、playFromMediaId リクエストの受信後にそのソースに切り替えます。

    AUX ツリー構造
    図 4. AUX ツリー構造

    それぞれの AUX MediaItem エントリには、「AUX」というフレーズを持たずローカライズされていないポート名に設定された、追加フィールド EXTRA_AUX_PORT_NAME があります。たとえば、「AUX 1」は「1」、「AUX front」は「front」、「AUX」は空の文字列に設定されます。英語圏以外では、名前タグは英語の文字列と同じままとなります。EXTRA_BCRADIO_BAND_NAME_EN とは異なり、値は OEM で定義され、事前定義されたリストで制約されていません。

    デバイスが AUX ポートに接続されているかどうかをハードウェアが検出できれば、入力が接続されている場合にのみ MediaItem を PLAYABLE としてマークします。このポートに何も接続されていない場合、ハードウェアは引き続き列挙を行う必要があります(ただし PLAYABLE ではありません)。ハードウェアにこのような機能がない場合、MediaItem は常に PLAYABLE に設定される必要があります。

    追加フィールド

    次のフィールドを定義します。

    • EXTRA_CD_TRACK = "android.media.extra.CD_TRACK"
    • EXTRA_CD_DISK = "android.media.extra.CD_DISK"
    • EXTRA_AUX_PORT_NAME = "android.media.extra.AUX_PORT_NAME"

    クライアントは、EXTRA_CD_DISK または EXTRA_AUX_PORT_NAME 追加フィールドがある要素の最上位の MediaItem を確認する必要があります。

    詳細な例

    次の例は、この設計に含まれるソースタイプの MediaBrowser ツリー構造に対応しています。

    ラジオ放送の MediaBrowserService ツリー構造(ACTION_PLAY_BROADCASTRADIO を処理します)。

    • ラジオ局(ブラウズ可能)EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_PROGRAMS
      • BBC One(再生可能)URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=90500
      • ABC 88.1(再生可能)URI: broadcastradio://program/RDS_PI/5678?AMFM_FREQUENCY=88100
      • ABC 88.1 HD1(再生可能)URI: broadcastradio://program/HD_STATION_ID_EXT/158241DEADBEEF?AMFM_FREQUENCY=88100&RDS_PI=5678
      • ABC 88.1 HD2(再生可能)URI: broadcastradio://program/HD_STATION_ID_EXT/158242DEADBEFE
      • 90.5 FM(再生可能) – RDS なしの FM URI: broadcastradio://program/AMFM_FREQUENCY/90500
      • 620 AM(再生可能)URI: broadcastradio://program/AMFM_FREQUENCY/620
      • BBC One(再生可能)URI: broadcastradio://program/DAB_SID_EXT/1E24102?RDS_PI=1234
    • お気に入り(ブラウズ可能、再生可能)EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_FAVORITES
      • BBC One(再生可能)URI: broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
      • BBC Two(再生不可)URI: broadcastradio://program/RDS_PI/1300?AMFM_FREQUENCY=102100
    • AM(ブラウズ可能、再生可能)EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="AM"
      • 530 AM(再生可能)URI: broadcastradio://program/AMFM_FREQUENCY/530
      • 540 AM(再生可能)URI: broadcastradio://program/AMFM_FREQUENCY/540
      • 550 AM(再生可能)URI: broadcastradio://program/AMFM_FREQUENCY/550
    • FM(ブラウズ可能、再生可能)EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="FM"
      • 87.7 FM(再生可能)URI: broadcastradio://program/AMFM_FREQUENCY/87700
      • 87.9 FM(再生可能)URI: broadcastradio://program/AMFM_FREQUENCY/87900
      • 88.1 FM(再生可能)URI: broadcastradio://program/AMFM_FREQUENCY/88100
    • DAB(再生可能)EXTRA_BCRADIO_FOLDER_TYPE=BCRADIO_FOLDER_TYPE_BANDEXTRA_BCRADIO_BAND_NAME_EN="DAB"

    オーディオ CD MediaBrowserService(ACTION_PLAY_AUDIOCD を処理):

    • ディスク 1(再生可能)EXTRA_CD_DISK=1
    • ディスク 2(ブラウズ可能、再生可能)EXTRA_CD_DISK=2
      • トラック 1(再生可能)EXTRA_CD_TRACK=1
      • トラック 2(再生可能)EXTRA_CD_TRACK=2
    • 音楽 CD(ブラウズ可能、再生可能)EXTRA_CD_DISK=3
      • All By Myself(再生可能)EXTRA_CD_TRACK=1
      • Reise, Reise(再生可能)EXTRA_CD_TRACK=2
    • 空スロット 4(再生不可)EXTRA_CD_DISK=4

    AUX MediaBrowserService(ACTION_PLAY_AUX を処理):

    • AUX 前面(再生可能)EXTRA_AUX_PORT_NAME="front"
    • AUX 背面(再生可能)EXTRA_AUX_PORT_NAME="rear"