无线电控制实现基于 MediaSession
和 MediaBrowse
,这使得媒体应用和语音助理应用能够控制无线电。如需了解详情,请参阅 developer.android.com 上的 为车载系统构建媒体应用。
car-broadcastradio-support 库的 packages/apps/Car/libs
中提供了媒体浏览树实现。此库还包含 ProgramSelector 的扩展程序,用于与 URI 之间进行转换。建议无线电实现使用此库来构建关联的浏览树。
媒体来源切换器
为了在无线电和媒体中显示的其他应用之间提供无缝过渡,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 以外的其他 UI,您可以创建一个自定义微件,以便在应显示切换器时启动 AppSelectionFragment
。
AppSelectionFragment newFragment = AppSelectionFragment.create(widget, packageName, fullScreen); newFragment.show(mActivity.getSupportFragmentManager(), null);
参考无线电应用实现(位于 packages/apps/Car/Radio
中)中提供了一个示例实现。
详细控制规范
MediaSession
(通过 MediaSession.Callback
)接口为当前正在播放的无线电节目提供了控制机制
onPlay
、onStop
。(取消)静音无线电播放。onPause
。时移暂停(如果支持)。onPlayFromMediaId
。播放顶层文件夹中的任何内容。例如,“播放 FM”或“播放无线电”。onPlayFromUri
。播放特定频率。例如,“播放 88.5 FM”。onSkipToNext
、onSkipToPrevious
。调到下一个或上一个电台。onSetRating
。添加到或从“收藏夹”中添加或移除。
MediaBrowser 通过三种类型的顶层目录公开可调谐的 MediaItem
- (可选)节目(电台)。此模式通常由双调谐器无线电使用,以指示用户所在位置所有可用的可调谐无线电台。
- 收藏夹。添加到“收藏夹”列表的无线电节目,有些可能不可用(超出接收范围)。
- 频段频道。当前区域中所有物理上可能的频道(87.9、88.1、88.3、88.5、88.7、88.9、89.1 等)。每个频段都有一个单独的顶层目录。

每个文件夹(AM/FM/节目)中的每个元素都是一个 MediaItem,其 URI 可与 MediaSession 结合使用进行调谐。每个顶层文件夹(AM/FM/节目)都是一个 MediaItem,其 mediaId 可与 MediaSession 结合使用以触发播放,并且由 OEM 自行决定。例如,“播放 FM”、“播放 AM”和“播放无线电”都是非特定的无线电查询,它们使用 mediaId 发送到 OEM 无线电应用。无线电应用可以自行决定从通用请求和 mediaId 中播放什么内容。
MediaSession
鉴于没有暂停广播流的概念,“播放”、“暂停”和“停止”操作并非始终适用于无线电。对于无线电,“停止”操作与将流静音相关联,而“播放”操作与取消静音相关联。
某些无线电调谐器(或应用)提供通过缓存内容并在稍后播放来模拟广播流暂停的功能。在这种情况下,请使用 onPause
。
从 mediaId 和 URI 播放操作旨在调谐到从 MediaBrowser 接口获取的电台。mediaId 是无线电应用提供的任意字符串,用于施加唯一(因此给定的 ID 仅指向一个项目)且稳定(因此给定的项目在整个会话中具有相同的 ID)的值,以便识别给定的电台。URI 将采用明确定义的架构。简而言之,URI 化的 ProgramSelector 形式。虽然这保留了唯一性属性,但它不必是稳定的,尽管当电台移动到不同的频率时,它可以更改。
根据设计,不使用 onPlayFromSearch
。客户端(配套应用)有责任从 MediaBrowser 树中选择搜索结果。将该责任转移到无线电应用会增加复杂性,需要在字符串查询应如何显示方面达成正式合同,并导致不同硬件平台上的用户体验不一致。
注意:无线电应用不包含任何额外信息,这些信息可能有助于搜索客户端未通过 MediaBrowser 接口公开的电台名称。
跳到下一个或上一个电台取决于当前上下文
- 当应用调谐到“收藏夹”列表中的电台时,应用可以从“收藏夹”列表中移动到下一个电台。
- 收听“节目”列表中的电台可能会导致调谐到下一个可用的电台,并按频道号排序。
- 收听任意频道可能会导致调谐到下一个物理频道,即使没有广播信号也是如此。
无线电应用会处理这些操作。
错误处理
TransportControls
操作(“播放”、“停止”和“下一个”)不提供有关操作是否成功的反馈。指示错误的唯一方法是将 MediaSession 状态设置为 STATE_ERROR
并显示错误消息。
无线电应用必须处理这些操作,并执行它们或设置错误状态。如果执行“播放”命令不是立即的,则播放状态应更改为 STATE_CONNECTING
(在直接调谐的情况下)或 STATE_SKIPPING_TO_PREVIOUS
或 NEXT
,同时正在执行命令。
客户端应观看 PlaybackState
并验证会话是否将当前节目更改为请求的节目或进入错误状态。STATE_CONNECTING
不得超过 30 秒。但是,直接调谐到给定的 AM/FM 频率应更快地执行。
添加和移除收藏夹
MediaSession 具有评分支持,可用于控制“收藏夹”。使用 RATING_HEART
类型的评分调用的 onSetRating
会将当前调谐的电台添加到“收藏夹”列表或从中移除。
与旧版预设相反,此模型假定为无序且无限制的“收藏夹”列表,其中每个保存的收藏夹都分配给一个数字槽(通常为 1 到 6)。因此,基于预设的系统与 onSetRating
操作不兼容。
MediaSession API 的限制在于,只能添加或移除当前调谐到的电台。例如,必须先选择项目,然后才能将其移除。这只是 MediaBrowser 客户端(如配套应用)的限制。无线电应用没有类似的限制。当应用不支持“收藏夹”时,此部分是可选的。
MediaBrowser
为了表达哪些频率或物理频道名称(当调谐到任意频道适用于给定的无线电技术时)对给定区域有效,列出了每个频段的所有有效频道(频率)。在美国区域,这相当于 101 个 FM 频道(范围为 87.8 到 108.0 MHz 范围,使用 0.2MHz 间距)和 117 个 AM 频道(范围为 530 到 1700 kHz,使用 10kHz 间距)。由于高清无线电使用相同的频道空间,因此不会单独呈现。
当前可用的无线电节目列表是扁平的,因为它不允许显示方案,例如按直接音频广播 (DAB) 组合分组。
“收藏夹”列表中的条目可能无法调谐。例如,如果给定节目超出范围。无线电应用可能会或可能不会预先检测到条目是否可以调谐。如果是这样,它可能不会将条目标记为可播放。
为了识别顶层文件夹,应用了与蓝牙相同的机制。也就是说, MediaDescription
对象的 Extras 软件包包含一个特定于调谐器的字段,就像蓝牙使用 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
。为了避免性能或 binder 相关的问题,MediaBrowser 服务必须支持分页
注意:默认情况下,分页在
onLoadChildren()
变体中默认实现,无需选项处理。所有类型的列表(原始频道、找到的节目和收藏夹)中的相关条目可能具有不同的 mediaId(这取决于无线电应用;支持库将使它们不同)。URI(以 ProgramSelector 形式)在原始频道和找到的节目之间有所不同(大多数情况下,除非 FM 没有 RDS),但在找到的节目和收藏夹之间大多相同(例如,除非 AF 已更新)。
为不同类型的列表中的条目设置不同的 mediaId 可以对它们执行不同的操作。您可以根据最近选择的
MediaItem
的文件夹,在onSkipToNext
上遍历“收藏夹”列表或“所有节目”列表(请参阅 MediaSession)。特殊调谐操作
“节目”列表允许用户调谐到特定电台,但不允许用户发出诸如“调谐到 FM”之类的通用请求,这可能会导致调谐到 FM 频段上最近收听的电台。
为了支持此类操作,某些顶层目录设置了
FLAG_PLAYABLE
标志(以及用于文件夹的FLAG_BROWSABLE
)。操作 调谐到 如何发出 播放无线电 任何无线电频道 startService(ACTION_PLAY_BROADCASTRADIO)
或
playFromMediaId(MediaBrowser.getRoot())
播放 FM 任何 FM 频道 从 FM 频段的 mediaId
播放。要调谐到哪个节目由应用决定。这通常是给定列表中最近调谐到的频道。有关
ACTION_PLAY_BROADCASTRADIO
的详情,请参阅通用播放 Intent。发现和服务连接
PackageManager
可以直接找到为广播无线电树提供服务的 MediaBrowserService。为此,请使用ACTION_PLAY_BROADCASTRADIO
Intent(请参阅通用播放 Intent)和MATCH_SYSTEM_ONLY
标志调用resolveService
。要查找为无线电服务的所有服务(可能不止一个;例如,单独的 AM/FM 和卫星),请使用queryIntentServices
。已解析的服务也处理
android.media.browse.MediaBrowserService
绑定 Intent。这已通过 GTS 验证。要连接到选定的 MediaBrowserService,请为给定的服务组件和
connect
创建MediaBrowser
实例。建立连接后,可以通过getSessionToken
获取 MediaSession 的句柄。无线电应用可以在其服务的
onGetRoot
实现中限制允许连接的客户端软件包。应用应允许系统应用在不列入许可名单的情况下连接。如需了解有关许可名单的详情,请参阅接受 Assistant 应用软件包和签名。如果特定于来源的应用(例如,无线电应用)安装在没有此类来源支持的设备上,它仍会声明自己正在处理
ACTION_PLAY_BROADCASTRADIO
Intent,但其 MediaBrowser 树将不包含特定于无线电的标记。因此,想要检查给定来源是否在设备上可用的客户端必须- 发现无线电服务(为
ACTION_PLAY_BROADCASTRADIO
调用resolveService
)。 - 创建
MediaBrowser
,然后连接到它。 - 确定是否存在带有
EXTRA_BCRADIO_FOLDER_TYPE
附加信息的MediaItem
。
注意: 在大多数情况下,客户端必须扫描所有可用的 MediaBrowser 树,以检测给定设备的所有可用来源。
频段名称
频段列表由一组顶层目录表示,这些目录的文件夹类型标记设置为
BCRADIO_FOLDER_TYPE_BAND
。它们的MediaItem
的标题是表示频段名称的本地化字符串。在大多数情况下,它将与英文翻译相同,但客户端不能依赖于该假设。为了为查找某些频段提供稳定的机制,为频段文件夹添加了一个额外的标记
EXTRA_BCRADIO_BAND_NAME_EN
。这是频段的非本地化名称,并且只能采用以下预定义的值之一AM
FM
DAB
如果频段不在列表中,则不应设置频段名称标记。但是,如果频段在列表中,则必须设置标记。高清无线电不枚举单独的频段,因为它使用与 AM/FM 相同的底层介质。
通用播放 Intent
每个专用于播放给定来源(如无线电或 CD)的应用都必须处理通用的播放 Intent,以从可能处于非活动状态(例如,启动后)开始播放某些内容。如何选择要播放的内容由应用决定,但通常是最近播放的无线电节目或 CD 曲目。为每个音频来源定义了单独的 Intent
android.car.intent.action.PLAY_BROADCASTRADIO
android.car.intent.action.PLAY_AUDIOCD
:CD-DA 或 CD-Textandroid.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
:本地媒体存储(内置闪存)
选择 Intent 用于通用播放命令,因为它们一次解决了两个问题:通用播放命令本身和服务发现。拥有此类 Intent 的额外好处是可以执行如此简单的操作,而无需打开 MediaBrowser 会话。
服务发现实际上是使用这些 Intent 解决的更重要的问题。服务发现的过程很容易且明确(请参阅发现和服务连接)。
为了使某些客户端实现更容易,还有另一种发出此类“播放”命令的方法(无线电应用也必须实现):使用根节点的 rootId(用作 mediaId)发出
playFromMediaId
。虽然 根节点 并非旨在可播放,但其 rootId 是一个任意字符串,可以使其可作为 mediaId 使用。但是,客户端不需要理解这种细微差别。ProgramSelector
虽然
mediaId
足以从MediaBrowserService
中选择频道,但它会绑定到会话,并且在提供商之间不一致。在某些情况下,客户端可能需要绝对指针(例如,绝对频率)以在会话和设备之间维护它。在数字无线电广播时代,仅频率不足以调谐到特定电台。因此,请使用
ProgramSelector
调谐到模拟或数字频道。ProgramSelector
由两部分组成- 主标识符。给定无线电台的唯一且稳定的标识符,它不会更改,但可能不足以调谐到该电台。例如,RDS PI 代码,在美国可以转换为呼号。
- 辅助标识符。有助于调谐到该电台的其他标识符(例如,频率),可能包括来自其他无线电技术的标识符。例如,DAB 电台可能具有模拟广播回退。
为了使
ProgramSelector
适合基于MediaBrowser
或MediaSession
的解决方案,请定义 URI 架构以对其进行序列化。架构定义如下broadcastradio://program/<primary ID type>/<primary ID>? <secondary ID type>=<secondary ID>&<secondary ID type>=<secondary 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
的授权部分(又名主机)为将来的架构扩展提供了一些空间。标识符类型字符串被精确地指定为其在 HAL 2.xIdentifierType
定义中的名称,并且值格式为十进制或十六进制(带有0x
前缀)数字。所有供应商特定的标识符都由
VENDOR_
前缀表示。例如,VENDOR_0
用于VENDOR_START
,VENDOR_1
用于VENDOR_START
加 1。此类 URI 特定于生成它们的无线电硬件,并且不能在不同 OEM 制造的设备之间传输。这些 URI 必须分配给顶层无线电文件夹下的每个 MediaItem。此外,MediaSession 必须同时支持
playFromMediaId
和playFromUri
。但是,URI 主要用于无线电元数据提取(如 FM 频率)和持久存储。不能保证 URI 将适用于所有媒体项目(例如,当框架尚不支持主要 ID 类型时)。另一方面,Media ID 始终有效。不建议客户端使用 URI 从当前 MediaBrowser 会话中选择项目。而是使用playFromMediaId
。也就是说,对于服务应用来说,它不是可选的,并且缺少 URI 保留用于有充分理由的情况。初始设计在架构部分之后使用单冒号而不是
://
序列。但是,前者不受android.net.Uri
用于绝对分层 URI 引用的支持。其他来源类型
可以类似地处理其他音频来源。例如,辅助输入和音频 CD 播放器。
单个应用可以服务于多种类型的来源。在这种情况下,建议您为每种类型的来源创建一个单独的 MediaBrowserService。即使在设置了多个服务来源/MediaBrowserService 的情况下,也强烈建议在单个应用中拥有单个 MediaSession。
音频 CD
类似于音频 CD,提供此类光盘的应用会公开 MediaBrowser,其中包含一个可浏览的条目(如果系统有 CD 换碟机,则可以有多个),该条目进而包含给定 CD 的所有曲目。如果系统不了解每张 CD 上的曲目(例如,当所有光盘一次性插入盒匣且系统未读取所有光盘时),则整张光盘的 MediaItem 将仅为
PLAYABLE
,而不是BROWSABLE
加PLAYABLE
。如果给定插槽中没有光盘,则该项目既不是PLAYABLE
也不是BROWSABLE
(但每个插槽必须始终存在于树中)。图 3. 音频 CD 树结构。 这些条目标记方式与广播电台文件夹类似;它们将包含 MediaDescription API 中定义的其他 extra 字段
EXTRA_CD_TRACK
:对于音频 CD 上的每个MediaItem
,从 1 开始的曲目号。EXTRA_CD_DISK
:从 1 开始的光盘号。
对于启用 CD-Text 的系统和兼容光盘,顶层 MediaItem 将具有光盘标题。同样,曲目的 MediaItem 将具有曲目标题。
辅助输入
提供辅助输入的应用会公开一个 MediaBrowser 树,其中包含一个条目(或多个条目,当存在多个端口时)来表示 AUX 输入端口。相应的 MediaSession 获取其 mediaId,并在收到
playFromMediaId
请求后切换到该源。图 4. AUX 树结构。 每个 AUX MediaItem 条目都将具有一个 extra 字段
EXTRA_AUX_PORT_NAME
,设置为不包含“AUX”短语的端口的非本地化名称。例如,“AUX 1”应设置为“1”,“AUX front”应设置为“front”,“AUX”应设置为空字符串。在非英语语言环境中,名称标记将保持相同的英语字符串。与EXTRA_BCRADIO_BAND_NAME_EN
不同,这些值是 OEM 定义的,不受预定义列表的约束。如果硬件可以检测到连接到 AUX 端口的设备,则仅当输入已连接时,硬件才应将 MediaItem 标记为
PLAYABLE
。如果此端口未连接任何设备,则仍应枚举硬件(但不是PLAYABLE
)。如果硬件不具备此功能,则 MediaItem 必须始终设置为PLAYABLE
。Extra 字段
定义以下字段
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"
客户端需要查看顶层 MediaItem,以查找设置了
EXTRA_CD_DISK
或EXTRA_AUX_PORT_NAME
extra 字段的元素。详细示例
以下示例介绍了此设计中源类型的 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 的 FMURI:
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
- BBC One(可播放)URI:
- 收藏夹(可浏览、可播放)
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
- BBC One(可播放)URI:
- 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
- 530 AM(可播放)URI:
- 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
- 87.7 FM(可播放)URI:
- 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
- 曲目 1(可播放)
- 我的音乐 CD(可浏览、可播放)
EXTRA_CD_DISK=3
- All By Myself(可播放)
EXTRA_CD_TRACK=1
- Reise, Reise(可播放)
EXTRA_CD_TRACK=2
- All By Myself(可播放)
- 空插槽 4(不可播放)
EXTRA_CD_DISK=4
AUX MediaBrowserService(处理
ACTION_PLAY_AUX
)- AUX front(可播放)
EXTRA_AUX_PORT_NAME="front"
- AUX rear(可播放)
EXTRA_AUX_PORT_NAME="rear"