This page explains how to implement radio at the hardware and software levels.
- System components illustrates and describes the radio technology stack.
- Broadcast Radio Hardware Abstraction Layer provides data structures and interfaces for OEMs to implement broadcast radio such as AM/FM and digital audio broadcasting (DAB) radio at the hardware level.
- Radio control implementation is based on
MediaBrowse, which enable Media and voice assistant apps to control the radio. In addition to the content provided below, see Build media apps for cars.
The broadcast radio stack includes the following components.
Radio reference app
For details about how to implement radio control, see Radio control implementation.
A sample Java radio app (
packages/apps/Car/Radio) serves as a reference
implementation. When the app service starts, it requests Radio Manager to open a Radio Tuner. Then,
the app can send requests to the Radio Tuner, such as tuning to a specific radio station, frequency,
or to seek the next available radio station. The app receives updates from the Radio Manager and
Radio Tuner in Radio, such as current program information, radio program lists, configurations, and
vendor-defined parameters. The reference Radio app only supports AM and FM radio. OEMs can modify or
replace the Radio app as desired.
When the app requests Radio Manager to open a tuner, the Radio Manager
frameworks/base/core/java/android/hardware/radio/RadioManager.java) requests the
Broadcast Radio Service to open a Tuner session and then wraps the session in a Radio Tuner
frameworks/base/core/java/android/hardware/radio/RadioTuner.java), which is returned
to the app.
The Radio Tuner defines APIs (such as tune, step, and cancel) that can be called from radio apps
and send requests to Broadcast Radio Service. Callback methods (
defined in Radio Tuner send updates about the broadcast radio HAL, such as current program
information, program lists, and vendor-defined parameters, from the Broadcast Radio Service to
Broadcast Radio Service
The Broadcast Radio Service
frameworks/base/services/core/java/com/android/server/broadcastradio) is the client
service for Broadcast Radio HAL. The Broadcast Radio Service coordinates multiple Radio Managers
with Broadcast Radio HALs. The Broadcast Radio Service supports
HAL interface definition language (HIDL) and
Android interface definition language (AIDL) broadcast
radio HALs. The Broadcast Radio Service links to the AIDL HAL when any AIDL HAL service exists;
otherwise, the service links to the HIDL HAL. The Broadcast Radio Service creates a Radio Module
for each available HAL instance (such as AM, FM, and DAB).
Each Radio Manager can request the Broadcast Radio Service to create a tuner session on the corresponding Radio Module, based on the type of radio. Each tuner session can call methods, such as tune, step, and cancel (defined in HAL interfaces) to perform operations on the corresponding broadcast radio HAL instance. When one tuner session receives a callback from the HAL instance on a HAL update, such as current program info, program list, configuration flags and, vendor parameters, callbacks about the update are sent to all Radio Tuners linked to the same Radio Module.
Broadcast radio HAL
To learn more about the HIDL and AIDL interfaces of broadcast radio and the differences between the two, see Broadcast radio HAL interface.
Broadcast Radio Hardware Abstraction Layer
The following sections describe how to work with the hardware layer to implement broadcast radio.
Broadcast radio HAL interface
The Broadcast radio HAL provides data structures and interfaces at the hardware level to implement broadcast radio, such as AM/FM and DAB radio.
HIDL 2.0 and AIDL interfaces
The broadcast radio HAL uses the interfaces described in the following sections.
IAnnouncementListener is the callback interface for the announcement listener, which
can be registered on broadcast radio HAL to receive announcements. The interface has the following
|Description: Called whenever the announcement list has changed.|
ICloseHandle is the generic close handle to remove a callback that doesn't need an
|Description: Close the handle.|
ITunerCallback is the callback interface called by the broadcast radio HAL to
send updates to HAL client service.
|Description: Called by the HAL when a tuning operation (tune, seek (in AIDL) or scan (in HIDL) and step succeeds) fails asynchronously.|
|Description: Called when tune, seek (in AIDL) or scan (in HIDL), or step succeeds.|
|Description: Called when tune, seek (in AIDL) or scan (in HIDL), or step succeeds.|
|Description: Called when the program list is updated; the size of each chunk should be limited to 500kiB.|
|Description: Called when the antenna is connected or disconnected.|
|Description: Called when the vendor-specific parameter values are updated internally in HAL (should
not be invoked after calling
|Description: New in AIDL. Called when the config flag is updated internally in the HAL (should not be
invoked after calling
|HIDL 2.0||Not applicable.|
Primary broadcast radio HAL interface
IBroadcastRadio is the primary interface for the broadcast radio HAL. In the HIDL
2.0 HAL, use the
ITunerSession interface to the tuner to call operations. However, at
most one tuner is active at one time (provided each broadcast radio HAL instance only has one tuner
ITunerSession was removed from the AIDL interfaces and its interfaces moved to
|Description: Get the description of a module and its capabilities.|
|Description: Fetches the current or possible AM/FM region configuration.|
|Description: Fetches the current DAB region configuration.|
|Description: Gets an image from the radio module cache. In AIDL, the image size must be less than 1MB due to a hard limit on the binder transaction buffer.|
|Description: Registers the announcement listener.|
|Description: Tunes to a specified program.|
|Description: Seeks the next valid program on the air. To avoid confusion in AIDL,
|Description: Steps to the adjacent channel, which may not be occupied by any program.|
|Description: Cancels pending tune, scan (in HIDL) or seek (in AIDL), or step operations.|
|Description: Applies a filter to the program list and starts sending program list updates over
|Description: Stops sending program list updates.|
|Description: Fetches the current setting of a given config flag.|
|Description: Sets the given config flag.|
|Description: Sets vendor-specific parameter values.|
|Description: Retrieves vendor-specific parameter values.|
Since each tuning operation (for example, tune, scan (in HIDL) or seek (in AIDL), and step) might be time-consuming and the thread should not be blocked for a long time, the operation should schedule time-consuming operations to occur later and quickly return a status or result. In detail, each operation should:
- Cancel all pending tuning operations.
- Check if the operation can be processed based on the method inputs and the status of the tuner.
- Schedule the tuning task and then return the
Result(in HIDL) or
status(in AIDL) immediately. If the
OK, the tuner callback
currentProgramInfoChangedmust be called when the tuning task has failed (for example, due to a timeout) or is complete.
startProgramListUpdates also schedules the time-consuming task of
updating the program list to take place later and to quickly return a status or result. The method
first cancels pending update requests and then schedules the updating task and quickly returns the
Due to the asynchronous behavior of tuning operations (for example, tune, scan (in HIDL) or seek
(in AIDL), and step), there exists a race condition between canceling the operation and the tuning
cancel is called after the HAL completes a tuning operation and before
the callback completes, the cancel can be ignored and the callback should complete and be received
by the HAL client.
stopProgramListUpdates is called after the HAL completes a program
list update and before the
onCurrentProgramInfoChanged callback completes,
stopProgramListUpdates can be ignored and the callback should complete.
Data size limit
Since there is a hard limit on the binder transaction buffer, the data limit for some interface methods passing data of a potentially large size are clarified in the AIDL HAL.
getImagerequires the image returned less than 1 MB.
chunkto be less than 500kiB. Larger program lists must be split by the HAL implementation into multiple chunks and sent through multiple callbacks.
Changes in AIDL HAL data structures
In addition to changes in the interfaces, these changes have been applied to the data structures defined in broadcast radio AIDL HAL, which takes advantage of the AIDL.
Constantenum is removed in AIDL and defined as const int in
ANTENNA_DISCONNECTED_TIMEOUT_MSis renamed to
ANTENNA_STATE_CHANGE_TIMEOUT_MS. A new const int
TUNER_TIMEOUT_MSis added. All tune, seek, and step operations must be completed within this time.
Deemphasisare removed in AIDL and defined as const int in
AmFmRegionConfig. Correspondingly, both
ProgramInfoare declared as int, a bit computation result of the respective flags. Meanwhile,
D75are renamed to
ProgramInfoFlagsare removed in AIDL and defined as const int in
ProgramInfowith a prefix
ProgramInfois declared as int, a bit computation result of flags.
TUNEDis also renamed to
FLAG_TUNABLE, to better describe its definition that the station can be tuned to.
scanSpacingis renamed to
scanis renamed to
- Since the concept of union is introduced in AIDL,
Metadatadefined in HIDL HAL are no longer used. An AIDL union
Metadatais defined in AIDL HAL. Each enum value previously in
MetadataKeyis now a field in
Metadatawith type of string or int, depending on their definitions.
Radio control implementation
Radio control implementation is based on
MediaBrowse, which enable Media and voice assistant apps to
control the radio. For more information, see
Build media apps for cars on developer.android.com.
A media browse tree implementation is provided in the car-broadcastradio-support
packages/apps/Car/libs. This library also contains extensions of
ProgramSelector to convert to and from URI. It is recommended that radio implementations
use this library to build the associated browse tree.
Media source switcher
To provide a seamless transition between radio and other apps displayed in media,
the car-media-common library contains classes that should be integrated into the radio
MediaAppSelectorWidgetcan be included in the XML for the radio app
(the icon and drop-down used in the reference media and radio apps):
<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" />
This widget launches the
AppSelectionFragment, which displays a
list of media sources that can be switched to. If a UI other than that provided is desired,
you can create a custom widget to launch the
AppSelectionFragment when the
switcher should be displayed.
AppSelectionFragment newFragment = AppSelectionFragment.create(widget, packageName, fullScreen); newFragment.show(mActivity.getSupportFragmentManager(), null);
A sample implementation is provided in the reference radio app implementation,
Detailed control specifications
interface provides control mechanisms for the currently playing radio program:
onStop. (Un)mute radio playback.
onPause. Time-shifted pause (if supported).
onPlayFromMediaId. Play any content from a top-level folder. For example, "Play FM" or "Play Radio."
onPlayFromUri. Play a specific frequency. For example, "Play 88.5 FM."
onSkipToPrevious. Tune to a next or previous station.
onSetRating. Add or remove to or from Favorites.
The MediaBrowser exposes a tunable MediaItem over three types of top-level directories:
- (Optional) Programs (stations). This mode is typically used by dual-tuner radios to indicate all available tuneable radio stations at the user's location.
- Favorites. Radio programs added to the Favorites list, some may be unavailable (out of reception range).
- Band channels. All physically possible channels in the current region (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.
Each element in each of these folders (AM/FM/Programs) is a MediaItem with a URI that can be used with MediaSession to tune. Each top-level folder (AM/FM/Programs) is a MediaItem with a mediaId that can be used with MediaSession to trigger playback and is up to the discretion of the OEM. For example, "Play FM," "Play AM," and "Play Radio" are all non-specific radio queries that use a mediaId to send to the OEM radio app. It's up to the radio app to determine what to play from the generic request and the mediaId.
Given there is no concept of pausing a broadcast stream, the Play, Pause, and Stop actions do not always apply to radio. With radio, the Stop action is associated with muting the stream while Play is associated with removing the mute.
Some radio tuners (or apps) provide the ability to simulate a broadcast stream pause by
caching content and then playing it back later. In such cases, use
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 impose a unique (so a given ID points to only one item) and stable (so a given item has the same ID through the whole session) value with which to identify a given station. The URI will be of a well-defined schema. In short, a URI-ized form of ProgramSelector. While this preserves the uniquity attribute, it need not be stable, although it can change when the station moves to a different frequency.
onPlayFromSearch is not used. Is is the responsibility of the client
(companion app) to select a search result from the MediaBrowser tree. Moving
that responsibility to the radio app would increase complexity, require formal contracts on how
string queries should appear, and result in an uneven user experience on different hardware
Note: The radio app does not contain additional information that would be useful to search for a station name not exposed to the client through the MediaBrowser interface.
Skipping to the next or previous station depends on the current context:
- When an app is tuned to a station from the Favorites list, the app can move to the next station from the Favorites list.
- Listening to a station from the Program list may result in tuning to the next available station, sorted according to channel number.
- Listening to an arbitrary channel may result in tuning to the next physical channel, even when there is no broadcast signal.
The radio app handles these actions.
actions (Play, Stop, and Next) doesn't provide feedback as to whether the action
succeeds or not. The only way to indicate an error is to set the MediaSession
with an error message.
The radio app must handle those actions and either execute them or set an error state.
If executing the Play command is not immediate, the playback state should be changed to
(in case of direct tune) or
while the command is being executed.
The client should watch the
and verify that the session changed the current program to what was requested or entered into the
STATE_CONNECTING must not exceed 30s. However, a direct tune to a given
AM/FM frequency should perform much faster.
Adding and Removing Favorites
MediaSession has rating support, which can be used to control Favorites.
called with a rating of type
adds or removes the currently tuned station to or from the Favorites list.
Contrary to legacy presets, this model assumes an unordered and unbounded Favorites
list, when each saved favorite was allocated to a numerical slot (typically, 1 to 6).
As a result, preset-based systems would be incompatible with
The limitation of the MediaSession API is that only the station currently tuned to can be added or removed. For example, items must be selected first before they can be removed. This is only a limitation of the MediaBrowser client, such as a companion app. The radio app is not similarly restricted. This portion is optional when an app doesn't support Favorites.
To express which 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. In the US region, this amounts to 101 FM channels from in the range of 87.8 to 108.0 MHz range (using 0.2MHz spacing) and 117 AM channels in the range of 530 to 1700 kHz (using 10kHz spacing). Because HD radio uses the same channel space, it is not presented separately.
The list of currently available radio programs is flat in that this doesn't allow display schemes such as grouping by direct audio broadcast (DAB) ensemble.
Entries on the Favorite list may not be tunable. For example if a given program is out of range. The radio app may or may not detect if the entry can be tuned to beforehand. If so, it may not mark the entry as playable.
To identify top-level folders, the same mechanism used by Bluetooth is applied.
That is, an Extras bundle of the
object contains a tuner-specific field just as Bluetooth does with
In the case of broadcast radio, this leads to defining the following new fields in the
EXTRA_BCRADIO_FOLDER_TYPE = "android.media.extra.EXTRA_BCRADIO_FOLDER_TYPE". One of the following values:
BCRADIO_FOLDER_TYPE_PROGRAMS = 1. Currently available programs.
BCRADIO_FOLDER_TYPE_FAVORITES = 2. Favorites.
BCRADIO_FOLDER_TYPE_BAND = 3. All physical channels for a given band.
There is no need to define any radio-specific custom metadata fields, as all the relevant data fits into the existing
- Program name (RDS PS, DAB service name).
- FM frequency. URI (see
MediaDescription.getTitle(if an entry is in the
- Radio-specific identifiers (RDS PI, DAB SId).
MediaDescription.getMediaUriparsed to ProgramSelector.
Typically, there is no need to fetch FM frequency for an entry on the current program or Favorites list (as the client should operate on media IDs). However, if such a need were to arise (for example, for display purposes), it's present in the URI and can be parsed to
ProgramSelector. That said, it's not recommended the URI be used to select items within the current session. For details, see
To avoid performance or binder-related issues, the MediaBrowser service must support pagination:
Note: By default, pagination is implemented by default in the
onLoadChildren()variant without options handling.
Related entries from all types of lists (raw channels, programs found and favorites) may have different mediaIds (it's up to the radio app; support library will have them different). The URIs (in ProgramSelector form) differ between raw channels and programs found in most cases (except for FM without RDS), but are mostly the same between programs found and favorites (except, for example, when AF was updated).
Having different mediaIds for entries from different types of lists makes it possible to take different actions on them. You can traverse either the Favorites list or the All Programs list on
onSkipToNext, depending on the folder of recently selected
Special tune actions
Program list enables users to tune to a specific station, but does not allow users to make general requests such as "Tune to FM", which could result in tuning to a recently listened to station on the FM band.
Action Tunes to How to issue Play radio Any radio channel
Play FM Any FM channel Play from the
mediaIdof the FM band.
The determination of which program to tune to is up to the app. This is typically the most recently tuned to channel from the given list. For details on
ACTION_PLAY_BROADCASTRADIO, see General play intents.
Discovery and service connection
PackageManagercan directly find the MediaBrowserService serving broadcast radio tree. To do so, call
ACTION_PLAY_BROADCASTRADIOintent (see General play intents) and
MATCH_SYSTEM_ONLYflag. To find all services that serve radio (there may be more than one; for example, separate AM/FM and satellite), use
The resolved service handles the
android.media.browse.MediaBrowserServicebind intent, too. This is verified with GTS.
To connect to the selected MediaBrowserService, create
MediaBrowserinstance for a given service component and
connect. After establishing the connection, a handle to MediaSession can be obtained via
The Radio app can restrict client packages allowed to connect in an
onGetRootimplementation of their service. The app should allow system apps to connect without whitelisting. For details about whitelisting, see Accept the Assistant app package and signature.
If the source-specific app (for example, a radio app) is installed on a device without such source support, it would still advertise itself as handling the
ACTION_PLAY_BROADCASTRADIOintent, but its MediaBrowser tree would not contain radio-specific tags. Thus, a client willing to check if a given source is available on a device, must:
- Discover the radio service (call
MediaBrowserand then connect to it.
- Determine the presence of
Note: In most cases, the client must scan all available MediaBrowser trees to detect all available sources for a given device.
Band list is represented by a set of top-level directories with a folder type tag set to
MediaItem's titles are localized strings representing band names. In most cases it will be the same as English translation, but the client can't depend on that assumption.
To provide a stable mechanism for looking up certain bands, an extra tag is added for band folders,
EXTRA_BCRADIO_BAND_NAME_EN. This is a non-localized name of the band and can only take one of these predefined values:
If the band is not on this list, the band name tag should not be set. However, if the band is on the list, it must have a tag set. HD radio doesn't enumerate separate bands as it uses the same underlying medium as AM/FM.
General play intents
Each app dedicated for playing given source (like radio or CD) must handle a general play intent to start playing some content possibly from inactive state (for example, after boot). It's up to the app how to select content to play, but it's usually the recently played radio program or CD track.There is a separate intent defined for each audio source:
android.car.intent.action.PLAY_AUDIOCD: CD-DA or CD-Text
android.car.intent.action.PLAY_DATADISC: Optical data disc like CD/DVD, but not CD-DA (may be Mixed Mode CD)
android.car.intent.action.PLAY_AUX: Without specifying which AUX port
android.car.intent.action.PLAY_USB: Without specifying which USB device
android.car.intent.action.PLAY_LOCAL: Local media storage (built-in flash)
Intents were chosen to be used for general play command, because they solve two problems at once: the general play command itself and service discovery. Additional benefit of having such intent would be a possibility to execute such simple action without opening MediaBrowser session.
Service discovery is actually the more important problem solved with these intents. Procedure for service discovery is easy and unequivocal this way (see Discovery and service connection).
To make some client implementations easier, there is an alternative way of issuing such Play command (that also has to be implemented by the radio app): issuing
playFromMediaIdwith the rootId of the root node (used as mediaId). While the root node is not meant to be playable, its rootId is an arbitrary string which can be made to be consumable as mediaId. However, clients are not required to understand this nuance.
mediaIdis enough to select a channel from the
MediaBrowserService, it becomes bound to a session and not consistent between providers. In some cases the client may need an absolute pointer (such as an absolute frequency) to maintain it between sessions and devices.
In the era of digital radio broadcasts, a bare frequency is not sufficient to tune to a specific station. Therefore, use
ProgramSelectorto tune to an analog or digital channel.
ProgramSelectorconsists 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, RDS PI code, which may be translated to the call sign in the US.
- Secondary identifiers. Additional identifiers useful for tuning to that station (for example, frequency), possibly including identifiers from other radio technologies. For example, a DAB station may have an analog broadcasting fallback.
ProgramSelectorto fit into the
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>
In this example, the secondary Identifiers portion (after the question mark (
?)) is optional and can be removed to provide a stable identifier for use as
mediaId. For example:
The authority part (AKA host) of
programprovides some room for scheme extension in the future. The identifier type strings are precisely specified as their names in the HAL 2.x definition of
IdentifierTypeand the value format is a decimal or hexadecimal (with
All vendor-specific identifiers are represented by the
VENDOR_prefix. For example,
VENDOR_STARTplus 1. Such URIs are specific to the radio hardware on which they were generated and cannot be transferred between devices made by different OEMs.
These URIs must be assigned to each MediaItem under the top-level radio folders. In addition, the MediaSession must support both
playFromUri. However, the URI is primarily intended for radio metadata extraction (such as FM frequency) and persistent storage. There is no guarantee the URI will be available for all media items (for example, when the primary ID type is not yet supported by the framework). On the other hand, Media ID always works. It is not recommended that clients use URI to select items from the current MediaBrowser session. Instead, use
playFromMediaId. That said, it is not optional for the serving app and missing URIs are reserved for well-justified cases.
The initial design used a single colon instead of the
://sequence after the scheme part. However, the former is not supported by
android.net.Urifor absolute hierarchical URI references.
Other source types
Other audio sources can be handled similarly. For example, auxiliary input and the Audio CD player.
A single app may serve multiple types of sources. In such cases, it's recommended you create a separate MediaBrowserService for each type of source. Even in a set-up with multiple served sources/MediaBrowserServices, it's strongly recommended to have a single MediaSession within a single app.
Similar to Audio CD in that the app that serves such disks would expose MediaBrowser with a single browsable entry (or more, if the system has a CD changer), which in turn would contain 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 once and it doesn't read them all), then MediaItem for the entire disk would be just
PLAYABLE. If there is no disk in a given slot, the item would be neither
BROWSABLE(but each slot must always be present in the tree).
These entries would be marked in a similar way that broadcast radio folders are; they would contain additional extra fields defined in the MediaDescription API:
EXTRA_CD_TRACK: For every
MediaItemon Audio CD, 1-based track number.
EXTRA_CD_DISK: 1-based disk number.
For CD-Text enabled system and compatible disk, the top-level MediaItem would have a title of the disk. Similarly, the MediaItems for tracks, would have a title of the track.
The app that serves auxiliary input exposes a MediaBrowser tree with a single entry (or more, when multiple ports exist) representing the AUX in port. The respective MediaSession takes its mediaId and switches to that source after getting the
Each AUX MediaItem entry would have an extra field
EXTRA_AUX_PORT_NAMEset to the non-localized name of the port without the "AUX" phrase. For example, "AUX 1" would have be set to "1", "AUX front" to "front" and "AUX" to an empty string. In non-English locales, the name tag would remain the same English string. Unlikely as for
EXTRA_BCRADIO_BAND_NAME_EN, the values are OEM-defined and not constrained to a predefined list.
If the hardware can detect devices connected to the AUX port, the hardware should mark the MediaItem as
PLAYABLE, only if input is connected. The hardware should still be enumerated (but not
PLAYABLE) if nothing was connected to this port. If the hardware has no such capability, the MediaItem must always be set to
Define the following fields:
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"
Client needs to review the top-level MediaItems for elements having the
EXTRA_AUX_PORT_NAMEextra field set.
The following examples address the MediaBrowser tree structure for source types that are part of this design.
Broadcast radio MediaBrowserService (handles
- Stations (browsable)
- BBC One (playable) URI:
- ABC 88.1 (playable) URI:
- ABC 88.1 HD1 (playable) URI:
- ABC 88.1 HD2 (playable) URI:
- 90.5 FM (playable) – FM without RDSURI:
- 620 AM (playable) URI:
- BBC One (playable) URI:
- BBC One (playable) URI:
- Favorites (browsable, playable)
- BBC One (playable) URI:
- BBC Two (not playable)URI:
- BBC One (playable) URI:
- AM (browsable, playable):
- 530 AM (playable) URI:
- 540 AM (playable) URI:
- 550 AM (playable) URI:
- 530 AM (playable) URI:
- FM (browsable, playable):
- 87.7 FM (playable) URI:
- 87.9 FM (playable) URI:
- 88.1 FM (playable) URI:
- 87.7 FM (playable) URI:
- DAB (playable):
Audio CD MediaBrowserService (handles
- Disc 1 (playable)
- Disc 2 (browsable, playable)
- Track 1 (playable)
- Track 2 (playable)
- Track 1 (playable)
- My music CD (browsable, playable)
- All By Myself (playable)
- Reise, Reise (playable)
- All By Myself (playable)
- Empty slot 4 (not playable)
AUX MediaBrowserService (handles
- AUX front (playable)
- AUX rear (playable)