Enable playback control

To enable media playback in Android Auto and Android Automotive OS (AAOS), implement playback controls by registering a media session and handling its callback methods. This page explains how to:

  • Register a MediaSessionCompat object in your media browser service.

  • Implement MediaSessionCompat.Callback methods to respond to user playback requests.

  • Configure standard and custom playback actions.

  • Set the initial playback state for your media session.

  • Add icons to indicate audio format.

  • Create links from actively playing media items.

Android Auto and AAOS send playback control commands through MediaSessionCompat for your service. You must register a session and implement the associated callback methods.

Register a media session

In your media browser service's onCreate method, create an instance of MediaSessionCompat, then call setSessionToken to register the media session. This code snippet shows how to create and register a media session:

Kotlin

override fun onCreate() {
    super.onCreate()
    ...
    // Start a new MediaSession.
    val session = MediaSessionCompat(this, "session tag").apply {
        // Set a callback object that implements MediaSession.Callback
        // to handle play control requests.
        setCallback(MyMediaSessionCallback())
    }
    sessionToken = session.sessionToken
    ...
}

Java

public void onCreate() {
    super.onCreate();
    ...
    // Start a new MediaSession.
    MediaSessionCompat session = new MediaSessionCompat(this, "session tag");
    setSessionToken(session.getSessionToken());

    // Set a callback object that implements MediaSession.Callback
    // to handle play control requests.
    session.setCallback(new MyMediaSessionCallback());
    ...
}

When you create the media session object, you set a callback object that is used to handle playback control requests. You create this callback object by providing an implementation of the MediaSessionCompat.Callback class for your app. The next section discusses how to implement this object.

Implement play commands

When a user requests playback for a media item from your app, Android Automotive OS and Android Auto use the MediaSessionCompat.Callback class from your app's MediaSessionCompat object that they obtained from your app's media browser service. When a user wants to control content playback, such as pausing playback or skipping to the next track, Android Auto and Android Automotive OS invoke one of the callback object's methods.

To handle content playback, your app must extend the abstract MediaSessionCompat.Callback class and implement the methods that your app supports.

Implement each of these callback methods that make sense for the type of content offered by your app:

onPrepare
AAOS invokes this method when the media source changes.
onPlay

Invoked when the user selects play without choosing a specific item. Your app must play its default content or, if playback was paused with onPause, your app resumes playback.

onPlayFromMediaId

Invoked when the user chooses to play a specific item. The method receives the ID that your media browser service assigned to the media item in the content hierarchy.

onPlayFromSearch

Invoked when the user chooses to play from a search query. The app must make an appropriate choice based on the search string that was passed in.

onPause

Invoked when the user chooses to pause playback.

onSkipToNext

Invoked when the user chooses to skip to the next item.

onSkipToPrevious

Invoked when the user chooses to skip to the previous item.

onStop

Invoked when the user chooses to stop play. Override these methods in your app to provide the chosen result. You needn't implement a method if its purpose isn't supported by your app. For example, if your app plays a livestream, such as a sports broadcast, you needn't implement onSkipToNext. Instead, use the default implementation of onSkipToNext.

Your app doesn't need any special logic to play content through the car's speakers. When your app receives a request to play content, it plays audio in the same way that content is played through a user's phone speakers or headphones. Android Auto and AAOS automatically send the audio content to the car's system to play over the car's speakers.

To learn more about playing audio content, see Media Player overview, Audio app overview, and the ExoPlayer overview.

Set standard playback actions

Android Auto and AAOS display playback controls based on the actions that are enabled in the PlaybackStateCompat object. By default, your app must support the following actions:

Your app can additionally support the following actions if they are relevant to the app's content:

In addition, you can optionally create a play queue for display to for the user. To do this, call the setQueue and setQueueTitle methods, enable the ACTION_SKIP_TO_QUEUE_ITEM action, and define the callback onSkipToQueueItem.

Also, add support for the Now playing icon, which is an indicator for what is playing. To do this, call the setActiveQueueItemId method and pass the ID of the playing item in the queue. You need to update setActiveQueueItemId whenever there is a queue change.

Android Auto and AAOS display buttons for each enabled action as well as the playback queue. When users click these buttons, the system invokes the corresponding callback from MediaSessionCompat.Callback.

Reserve unused space

Android Auto and AAOS reserve space in the UI for the ACTION_SKIP_TO_PREVIOUS and ACTION_SKIP_TO_NEXT actions. If your app does not support one of these functions, Android Auto and AAOS use the space to display any custom actions you create.

If you don't want to fill those spaces with custom actions, you can reserve them so that Android Auto and AAOS leave the space blank whenever your app doesn't support the corresponding function.

To do so, call the setExtras method with an extras bundle that contains constants that correspond to the reserved functions. SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT corresponds to ACTION_SKIP_TO_NEXT, and SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV corresponds to ACTION_SKIP_TO_PREVIOUS. Use these constants as keys in the bundle, and use the boolean true as values.

Set initial PlaybackState

As Android Auto and AAOS communicate with your media browser service, your media session communicates the status of content playback using the PlaybackStateCompat.

Your app shouldn't automatically start playing music when AAOS or Android Auto connects to your media browser service. Instead, rely on Android Auto and AAOS to resume or start playback based on the car's state or user actions.

To accomplish this, set the initial PlaybackStateCompat of your media session to STATE_STOPPED, STATE_PAUSED, STATE_NONE, or STATE_ERROR.

Media sessions within Android Auto and AAOS only last for the duration of the drive, so users start and stop these sessions frequently. To promote a seamless experience between drives, keep track of the user's previous session state, so that when the media app receives a resume request, the user can automatically pick up where they left off. For example, the last played media item, the PlaybackStateCompat, and the queue.

Add custom playback actions

You can add custom playback actions to display additional actions that your media app supports. If space permits (and you don't reserve it), Android adds the custom actions to the transport controls. Otherwise, the custom actions appear in the Overflow menu. Android displays custom actions in the order you add them to PlaybackStateCompat.

Use custom actions to provide behavior distinct from standard actions. Don't use them to replace or duplicate standard actions.

To add custom actions, use the addCustomAction method in the PlaybackStateCompat.Builder class. This code snippet shows how to add a custom action to "Start a radio channel":

Kotlin

val customActionExtras = Bundle()
customActionExtras.putInt(
  androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
  androidx.media3.session.CommandButton.ICON_RADIO)

stateBuilder.addCustomAction(
    PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon // or R.drawable.media3_icon_radio
    ).run {
        setExtras(customActionExtras)
        build()
    }
)

Java

Bundle customActionExtras = new Bundle();
customActionExtras.putInt(
  androidx.media3.session.MediaConstants.EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT,
  androidx.media3.session.CommandButton.ICON_RADIO);

stateBuilder.addCustomAction(
    new PlaybackStateCompat.CustomAction.Builder(
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA,
        resources.getString(R.string.start_radio_from_media),
        startRadioFromMediaIcon) // or R.drawable.media3_icon_radio
    .setExtras(customActionExtras)
    .build());

For a more detailed example of this method, see the setCustomAction method in the Universal Android Music Player sample app on GitHub. After you create your custom action, your media session can respond to the actions by overriding the onCustomAction method.

This code snippet shows how your app could respond to a "Start a radio channel" action:

Kotlin

override fun onCustomAction(action: String, extras: Bundle?) {
    when(action) {
        CUSTOM_ACTION_START_RADIO_FROM_MEDIA -> {
            ...
        }
    }
}

Java

@Override
public void onCustomAction(@NonNull String action, Bundle extras) {
    if (CUSTOM_ACTION_START_RADIO_FROM_MEDIA.equals(action)) {
        ...
    }
}

To learn more, see the onCustomAction method in the Universal Android Music Player sample app on GitHub.

Create icons for custom actions

Each custom action that you create requires an icon.

If the description of that icon matches one of the CommandButton.ICON_ constants, set the integer value for the EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT key of the custom action's extras. On supported systems, doing so overrides the icon resource passed to CustomAction.Builder, allowing system components to consistently render your action and other playback actions.

You must also specify an icon resource. Apps in cars can run on many different screen sizes and densities, so icons that you provide must be vector drawables. Use a vector drawable to scale assets without losing detail. A vector drawable can align edges and corners to pixel boundaries at smaller resolutions.

If a custom action is stateful (if it toggles a playback setting on or off), provide different icons for the different states to help users see a change when they select the action.

Provide alternative icon styles for disabled actions

When a custom action is unavailable for the current context, swap the custom action icon with an alternative icon that shows the action as disabled.

Samples of off-style custom action icons.
Figure 1. Samples of off-style custom action icons.

Indicate audio format

To indicate that the playing media uses a special audio format, you can specify icons that are rendered in cars that support this feature. You can set the KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI and the KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI in the extras bundle of the currently playing media item (passed to MediaSession.setMetadata). Set both extras to accommodate the different layouts.

In addition, you can set the KEY_IMMERSIVE_AUDIO extra to tell car OEMs that this is immersive audio, and they should be very careful when deciding whether to apply audio effects that might interfere with the immersive content.

You can configure the playing media item so its subtitle, description, or both are links to other media items. That lets the user jump quickly to related items; for example, they might jump to other songs by the same artist or to other episodes of a podcast. If the car supports this feature, users can tap the link to browse to that content.

To add links, configure the KEY_SUBTITLE_LINK_MEDIA_ID metadata (to link from the subtitle) or KEY_DESCRIPTION_LINK_MEDIA_ID (to link from the description). For details, see the reference documentation for those metadata fields.