Google is committed to advancing racial equity for Black communities. See how.

Implementing Radio

The Radio control implementation is based on MediaSession and MediaBrowse, which together enable Media and Assistant to control the Radio. For details about these interfaces, see Build Android media apps for cars.

See the Media browse tree implementation in the car-broadcastradio-support library provided in packages/apps/Car/libs. This library also provides extensions of ProgramSelector to convert to and from URI. To implement radio, use this library to build a browse tree.

Media Source Switcher

To provide a seamless transition between Radio and other apps in Media, the car-media-common library defines Android intents and resources that can be used to implement a Media Source switcher button to launch a media source selector. A detailed explanation of these intents and the support provided by car-media-common can be found at Media.

A Radio UI replacement can include this switch by adding the following code to the onCreate() method. This code presumes the application is using the car-ui-lib toolbar).

private void updateMenuItems() {
        Intent appSelectorIntent = MediaSource.getSourceSelectorIntent(this, false);
        MenuItem appSelectorMenuItem = MenuItem.builder(this)
                .setOnClickListener(m -> startActivity(appSelectorIntent))

                /* other menu items */ ...,

Note: The ic_app_switch drawable is also provided by car-media-common, and should be used to ensure that Media Center and Radio display the same icon.

A sample is provided in the reference Radio app implementation located in packages/apps/Car/Radio.

Integrating with Assistant

In addition to the basic MediaSession implementation, the Radio app must implement the following MediaSession callbacks:

Mechanism Description
onPlay Start playing the currently selected radio station.
onStop Stop playing the radio.
onSkipToNext Tune to the next station.
onSkipToPrevious Tune to the previous station.
onSetRating Add and remove to and from Favorites.
onPlayFromMediaId Play the radio station corresponding to the provided media Id, which would correspond to a media item listed through the MediaBrowserService.
onPlayFromUri Play the radio station corresponding to the provided URI. A URI parser is provided as part of the car-broadcastradio-support library, which can be used to convert a URI to a ProgramSelector.

The MediaBrowser exposes these MediaItems that can be tuned to over these three types of top-level directories:

  • Programs (stations). Radio programs that are available to listen to and are in range.
  • Favorites. Radio programs that have been added to Favorites list. Some may be out of range and unavailable.
  • Band channels. All physically possible channels in the current region (for example, 87.9, 88.1, 88.3, 88.5, 88.7, 88.9, 89.1 and so on). Every band has a separate top-level directory.

Broadcast radio MediaBrowserService tree structure

Figure 1. Broadcast radio MediaBrowserService tree structure

Every element of these lists has a mediaId that can be used with MediaSession to tune.

Since the MediaBrowserService implementation is non-trivial, it is highly recommended that you use the provided car-broadcastradio-support library to delegate implementation details from the OEM Radio app. The OEM may still elect to implement these specifications.


The Play, Pause, and Stop actions do not perfectly apply to Radio due to the fact a broadcast stream cannot be paused. Instead, the Stop action can be regarded as *muting* a stream and Play as then unmuting the stream.

Some Radio apps simulate the pause of a broadcast stream by caching content and then playing it back via the onPause command. Otherwise, do not advertise that ACTION_PAUSE is supported.

Playing from mediaId and URI actions is intended to tune to a station fetched from the MediaBrowser interface. The mediaId is an arbitrary string provided by the Radio app to uniquely (so a given ID points to only one item) and with stability (so a given item has the same ID through the whole session) identify a given station. In this case, the URI will be of a well-defined schema, a form of ProgramSelector made to perform like a URI. The uniquity attribute is preserved though it need not be stable. (The attribute may change if the station moves to a different frequency).

onPlayFromSearch is not used. It is the client's responsibility (Google Assistant or companion app) to select a search result from the MediaBrowser tree. Shifting that responsibility to the Radio app increases complexity, requires formal contracts on the appearance of string queries, and results in an unpredictable user experience across different hardware platforms.

Skipping to the next or previous stations depends on the current context:

  • When an application is tuned to a station from the Favorites list, the Radio app can move to the next station from the list of Favorites.
  • Listening to a station from a program list may result in tuning to the next available station, sorted according to station.
  • Listening to a random station may result in tuning to the next physical station, perhaps without detecting a valid broadcast.

The Radio app must handle these scenarios.

Handling errors

TransportControls actions (Play, Stop, and Next) don't provide any feedback as to success or failure. To indicate an error requires that the MediaSession state be to STATE_ERROR with an error message.

The Radio app must either execute these actions or set the error state. If execution of the Play command is not immediate, change the playback state to STATE_CONNECTING (in the case of a direct tune) or STATE_SKIPPING_TO_PREVIOUS or STATE_SKIPPING_TO_NEXT while the command executes.

To verify that the session changed the current program to what was requested or was placed into the error state. the client should watch the PlaybackState. While STATE_CONNECTING should require no more than 30 seconds, directly tuning to a specific AM/FM frequency should occur much more quickly.

Adding and removing favorites

MediaSession has rating support, which can be used to control Favorites. onSetRating called with a rating of the type RATING_HEART adds or removes the currently tuned station to and from the Favorites list.

This model assumes an unordered and unbounded Favoritea list, contrary to legacy presets,in which each saved favorite was allocated to a numerical slot (usually 1 through 6). As a result, preset-based systems are incompatible with onSetRating.

The MediaSession API is limited in that it can only add and remove the currently tuned station. For example, an item can't be removed from the list without it first being selected. This is a limitation of the MediaBrowser client (such as Assistant or Companion app). The Radio app is not restricted.

Important: If an app doesn't support Favorites, the following content is optional.


To specify what frequencies or physical channel names (when tuning to an arbitrary channel is suitable for a given radio technology) are valid for a given region, all valid channels (frequencies) are listed for each band. The US region provides:

Band Channels
FM 101 channels in the 87.8 to 108.0 MHz range, with 0.2 MHz spacing.
AM 117 channels in the 530 to 1700 kHz range, with 10 kHz spacing.

HD radio uses the same channel space and is therefore not presented separately.

The list of currently available radio programs is flat, which doesn't enable display schemes such as grouping by Digital Audio Broadcasting (DAB) ensemble.

Entries in a Favorites list may sometimes be out of range. The Radio app may (or may not) detect that an entry can be tuned to beforehand. If so, the Radio app may not mark the entry as playable.

To identify the top level folders, use the same mechanism as is used by Bluetooth.

The Extras bundle of the MediaDescription object will contain a tuner-specific field, which is what Bluetooth does with EXTRA_BT_FOLDER_TYPE. In the case of broadcast radio, this requires the following new fields be defined in the public API.

EXTRA_BCRADIO_FOLDER_TYPE =, one of the following values:

Field Value
BCRADIO_FOLDER_TYPE_PROGRAMS 1. Currently available programs.
BCRADIO_FOLDER_TYPE_BAND 3. All physical channels for a given band.

Radio-specific custom metadata fields need not be set as all relevant data can be accommodated in the existing MediaBrowser.MediaItem scheme:

  • Program name (RDS PS, DAB service name). MediaDescription.getTitle
  • FM frequency. URI (see ProgramSelector section) or MediaDescription.getTitle. If the BROADCASTRADIO_FOLDER_TYPE_BAND folder contains an entry.
  • Radio-specific identifiers (RDS PI, DAB SId). MediaDescription.getMediaUri parsed to ProgramSelector.

Typically, you need not fetch the FM frequency for the entry on the current program or Favorites list (as the client should operate on media IDs). However, if such a need exists (perhaps for display purposes), the entry is present in the URI, which can be parsed to ProgramSelector.

The use of a URI for item selection within the current session is not recommended. For details, see ProgramSelector.

To avoid performance and binder-related issues, the MediaBrowser service must support pagination. Use the EXTRA_PAGE and EXTRA_PAGE_SIZE parameters for subscribe().

Related entries from all list types (raw channels, programs found, and Favorites) may have different media IDs, as determined by the Radio app. The support library will differ. The URIs (in ProgramSelector form) differs between raw channels and programs found in most cases, except for FM without RDS). For the most part, the same is true between programs found and Favorites (except, when AF got updated).

Entries from the frequency list have AMFM_FREQUENCY as their primary identifier, while entries from the program list most likely have RDS_PI (or another digital radio identifier type) as the primary identifier.

Having different media IDs for entries from different types of lists makes it possible to take different actions on them: traverse either favorites list or all programs list on onSkipToNext depending on what was the folder of recently selected MediaItem (see MediaSession section). It would also mean that assistant will unintentionally switch these contexts depending on where it finds the matching entry (in the favorite list or all available programs list). It's up to the app how to handle such cases.

Special tune actions

Program list allows to tune to a specific station, but does not allow to make general requests like "tune to FM", which might result in tuning to a recently listened station on the FM band. To support such actions, some top-level directories have the FLAG_PLAYABLE flag set, as well as the obligatory FLAG_BROWSABLE for folders.

Action Tunes to How to issue
Play Radio Any startService(ACTION_PLAY_BROADCASTRADIO) or,
Play Favorites Any Favorite Play from the mediaId in the favorites folder
Play FM Any FM channel Play from the mediaId of the FM band

The decision as to which exact program to tune to is determined by the application. It's usually the recently tuned channel from the given list. For details on ACTION_PLAY_BROADCASTRADIO, see General Play intents.

Discovery and service connection

PackageManager can directly find MediaBrowserService serving the broadcast radio tree. Just call resolveService with ACTION_PLAY_BROADCASTRADIO intent (see General Play intents) and the MATCH_SYSTEM_ONLY flag. To find all services that serve Radio (there may be more than one, for example separate AM/FM and satellite), use queryIntentServices. The resolved service will handle bind intent, too, as it is verified with GTS.

To connect to the selected MediaBrowserService, create MediaBrowser instance for a given service component and connect. After establishing the connection, a handle to MediaSession can be obtained via getSessionToken.

The Radio app can restrict client packages allowed to connect in onGetRoot implementation of their service. The app should allow system apps to connect without whitelisting.

If the source-specific application (for example, a Radio app) is installed on a device without such source support, it would still advertise itself as handling ACTION_PLAY_BROADCASTRADIO intent while the respective MediaBrowser tree would not contain radio-specific tags (which could be true should the same system image be flashed across different hardware devices).

A client can check that a given source is available on a device as follows:

  1. To discover the Radio service, call resolveService for ACTION_PLAY_BROADCASTRADIO).
  2. Create and connect to a MediaBrowser for the Radio service.
  3. Confirm any MediaItem with EXTRA_BCRADIO_FOLDER_TYPE extra.

In most cases, the client must review the available MediaBrowser trees to identify all available sources for a given device.

Band names

Band list is represented by a set of top-level directories with a folder type tag set to BCRADIO_FOLDER_TYPE_BAND. The titles of the respective MediaItem are localized strings representing band names. In most cases, the strings are the same as for the English translation. However, the client should assume so.

To provide a stable mechanism for looking up specific bands, an extra tag is added for band folders. EXTRA_BCRADIO_BAND_NAME_EN is the non-localized name of the band, which takes one predefined value:

  • TAM
  • FM
  • DAB
  • SXM
  • If the band is not on this list, it should not have a band name tag set. However, if the band is on the list, a tag must be set. HD Radio doesn't enumerate separate bands because HD Radio uses the same underlying medium as AM/FM.

    General Play intents

    Each app dedicated to playing a specific source (such as Radio or CD) must handle a general play intent to start playing content, possibly from aninactive state. For example, after a boot). It's up to the app how to select content to play, but it's usually the recently played Radio app or CD track.

    A separate intent is defined for each audio source:

    Intent Description CD-DA or CD-Text. Optical data disc such as CD and DVD, but not CD-DA. This may be Mixed Mode CD. Without specifying an AUX port. Without specifying a USB device. Local media storage, such as a built-in Flash drive.

    Intents were selected for use with the Play command, because intents address two purposes.

    Play command Service discovery

    An additional benefit is that an intent can execute a simple action without opening a MediaBrowser session. That stated, service discovery is the more valuable purpose addressed by intents. The procedure for service discovery thus becomes straight-forward and unequivocal. For details, see "Discovery and service connection".

    To simplify client implementations, an alternative to the issuing of the Play command (that also has to be implemented by the Radio app) is to issue playFromMediaId1 with therootIdof the root node (used as mediaId). While the root node is not meant to be playable, itsrootId is an arbitrary string that can be designated as consumable as a mediaId.


    While mediaId is sufficient to select a channel from the MediaBrowserService, it's bound to a session and not consistent between providers. In some cases, the client may need an absolute pointer (like an absolute frequency) to maintain it between sessions and devices.

    In this era of DABs, a single frequency is insufficient to tune to a specific station. To provide an entity that can be used to tune to either an analog or a digital channel, ProgramSelector was designed. (design doc, HAL sources, Java sources). This entity consists of two parts:

    • Primary identifier. A unique and stable identifier for a given radio station that doesn't change, but may not be enough to tune to that station. For example, an RDS PI code, which may be translated to the call sign in the US.
    • Secondary identifiers. Any additional identifiers useful for tuning to the station. For example, frequency, possibly including identifiers from other radio technologies. A DAB station may fallback on analog broadcasting.

    To make ProgramSelector fit into this MediaBrowser/MediaSession-based solution, define a URI schema to serialize it. The schema is defined as follows:

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

    Where the secondary identifiers (which appear after the ? character) are optional and can be removed to provide a stable identifier for use as a mediaId. For example:

    • 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

    The authority portion (also known as host) of the program provides some room to extend the scheme in the future.

    Identifier type strings are precisely specified since the names in the HAL 2.x definition of IdentifierType and the value format is a decimal or hexadecimal (with a 0x prefix) number.

    All vendor-specific identifiers are represented with a VENDOR_ prefix. For example,VENDOR_0 for VENDOR_START and VENDOR_1 for VENDOR_START + 1.

    The initial design used a single colon (:) instead of the :// sequence after the scheme part. However, the does not support absolute hierarchical URI references.

    Other source types

    Other audio sources can be managed similarly to radio. For example, auxiliary input and Audio CD player.

    A single application may serve several types of sources. In such cases, it's highly recommended that a separate MediaBrowserService be created for each kind of source. Even in a set-up with multiple served sources or MediaBrowserServices, it's highly recommended you have a single MediaSession within a single application.

    Audio CD

    The application that serves such disks exposes the MediaBrowser with a single browsable entry (or more, if the system has a CD changer), which in turn contains all tracks of a given CD. If the system does not have the knowledge about the tracks on every CD (for example, when all disks are inserted in a cartridge at one time and they have not all been read), then MediaItem for the entire disk is just PLAYABLE, and not BROWSABLE and PLAYABLE. If no disk occupies a given slot, the item is neither PLAYABLE nor BROWSABLE, although each slot must always be present in the tree).

    Audio CD MediaBrowser tree structure

    Figure 2. Audio CD MediaBrowser tree structure

    These entries would be marked in a similar way that broadcast radio folders are – they would contain additional extra fields defined in MediaDescription API:

    Field Value
    EXTRA_CD_TRACK For every MediaItem on Audio CD, 1-based track number.
    EXTRA_CD_DISK 1-based disk number.

    For a CD-Text enabled system and compatible disk, the top-level MediaItem has the title of the disk. Similarly, MediaItems for tracks have the title of the track.

    Auxiliary input

    The application that serves auxiliary input exposes a MediaBrowser tree with a single entry (or more, if there are multiple ports) to represent the AUX In port. The respective MediaSession takes its mediaId and switches to that source after receicing a playFromMediaId request.

    AUX MediaBrowser tree structure

    Figure 3. AUX MediaBrowser tree structure

    Each AUX MediaItem entry has an extra field EXTRA_AUX_PORT_NAME set to the non-localized name of the port without the "AUX" phrase. For example "AUX 1" would be set to "1", "AUX front" to "front", and "AUX" to an empty string. In non-English locales, the name tag remains the same as the English string. This is unlikely for EXTRA_BCRADIO_BAND_NAME_EN, for which values are OEM-defined and not constrained to a predefined list.

    If the hardware can check if a device is connected to the AUX port, the hardware should mark the MediaItem as PLAYABLE only when an input is connected. The hardware must still enumerate (though not PLAYABLE) if nothing sis connected to this port. If the hardware has no such capability, the MediaItem must always be PLAYABLE.

    Extra fields

    The following extra keys can be defined:

    Field Value

    The client must look up the top-level MediaItems for those elements with values set for EXTRA_CD_DISK and EXTRA_AUX_PORT_NAME.

    Detailed Examples

    The following examples address the MediaBrowser tree structure for those source types included in this design.

    Broadcast radio MediaBrowserService handles ACTION_PLAY_BROADCASTRADIO.

    Stations (browsable) URI
    BBC One (playable) broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=90500
    ABC 88.1 (playable) broadcastradio://program/RDS_PI/5678?AMFM_FREQUENCY=88100
    ABC 88.1 HD1 (playable) broadcastradio://program/HD_STATION_ID_EXT/158241DEADBEEF?AMFM_FREQUENCY=88100&RDS_PI=5678
    ABC 88.1 HD2 (playable) broadcastradio://program/HD_STATION_ID_EXT/158242DEADBEFE
    90.5 FM (playable)
    FM without RDS
    620 AM (playable) broadcastradio://program/AMFM_FREQUENCY/620
    BBC One (playable) broadcastradio://program/DAB_SID_EXT/1E24102?RDS_PI=1234
    Favorites (browsable, playable) URI
    BBC One (playable) broadcastradio://program/RDS_PI/1234?AMFM_FREQUENCY=101300
    BBC Two (not playable) broadcastradio://program/RDS_PI/1300?AMFM_FREQUENCY=102100
    AM (browsable, playable) URI
    530 AM (playable) broadcastradio://program/AMFM_FREQUENCY/530
    540 AM (playable) broadcastradio://program/AMFM_FREQUENCY/540
    550 AM (playable) broadcastradio://program/AMFM_FREQUENCY/550
    FM (browsable, playable) URI
    87.7 FM (playable) broadcastradio://program/AMFM_FREQUENCY/87700
    87.9 FM (playable) broadcastradio://program/AMFM_FREQUENCY/87900
    88.1 FM (playable) broadcastradio://program/AMFM_FREQUENCY/88100
    Audio CD MediaBrowserService URI
    Disc 1 (playable) EXTRA_CD_DISK=1
    Disc 2 (browsable, playable) EXTRA_CD_DISK=2
  • Track 1 (playable) EXTRA_CD_TRACK=1
  • Track 2 (playable) EXTRA_CD_TRACK=2
  • My music CD (browsable, playable) EXTRA_CD_DISK=3
  • All By Myself (playable) EXTRA_CD_TRACK=1
  • Reise, Reise (playable) EXTRA_CD_TRACK=2
  • Empty slot 4 (not playable) EXTRA_CD_DISK=4
    AUX MediaBrowserService URI
    AUX front (playable) EXTRA_AUX_PORT_NAME="front"
    AUX rear (playable) EXTRA_AUX_PORT_NAME="rear"