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`](https://developer.android.com/reference/android/support/v4/media/session/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`](https://developer.android.com/reference/androidx/media/MediaBrowserServiceCompat#onCreate()) method, create an instance of
[`MediaSessionCompat`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat), then call [`setSessionToken`](https://developer.android.com/reference/androidx/media/MediaBrowserServiceCompat#setSessionToken(android.support.v4.media.session.MediaSessionCompat.Token)) 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`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback)
class for your app. The next section discusses how to implement this object.
| **Note:** To inspect the media session run `adb shell dumpsys media_session`

## 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`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback)
class from your app's [`MediaSessionCompat`](https://developer.android.com/reference/android/support/v4/media/session/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`](https://developer.android.com/reference/android/media/session/MediaSession.Callback#onPrepare())
:   AAOS invokes this method when the media source changes.

[`onPlay`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#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`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#onPause()), your app resumes playback.

    | **Note:** Your app mustn't automatically start playing music when Android Automotive OS or Android Auto connect to the media browser service. To learn more, see [Set initial PlaybackState](https://developer.android.com/training/cars/media/enable-playback#initial-playback-state).

[`onPlayFromMediaId`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#onPlayFromMediaId(java.lang.String,android.os.Bundle))

:   Invoked when the user chooses to play a specific item. The method receives
    the [ID](https://developer.android.com/cars/media/create-media-browser/content-hierarchy#onLoadChildren) that your media browser service assigned to the media item in the
    content hierarchy.

[`onPlayFromSearch`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#onPlayFromSearch(java.lang.String,android.os.Bundle))

:   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`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#onPause())

:   Invoked when the user chooses to pause playback.

[`onSkipToNext`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#onSkipToNext())

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

[`onSkipToPrevious`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#onSkipToPrevious())

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

[`onStop`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#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](https://developer.android.com/guide/topics/media/mediaplayer),
[Audio app overview](https://developer.android.com/training/managing-audio), and the ExoPlayer [overview](https://developer.android.com/guide/topics/media/exoplayer).

## Set standard playback actions

Android Auto and AAOS display playback controls based on the
actions that are enabled in the [`PlaybackStateCompat`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat)
object. By default, your app must support the following actions:

- [`ACTION_PLAY`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#ACTION_PLAY())
- [`ACTION_PAUSE`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#ACTION_PAUSE())
- [`ACTION_STOP`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#ACTION_STOP())
- [`ACTION_PLAY_FROM_MEDIA_ID`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID())
- [`ACTION_PLAY_FROM_SEARCH`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH())

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

- [`ACTION_SKIP_TO_PREVIOUS`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS())
- [`ACTION_SKIP_TO_NEXT`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#ACTION_SKIP_TO_NEXT())

In addition, you can optionally create a play queue for display to for the user.
To do this, call the [`setQueue`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat#setQueue(java.util.List%3Candroid.support.v4.media.session.MediaSessionCompat.QueueItem%3E)) and [`setQueueTitle`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat#setQueueTitle(java.lang.CharSequence)) methods, enable
the [`ACTION_SKIP_TO_QUEUE_ITEM`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM()) action, and define the callback
[`onSkipToQueueItem`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#onSkipToQueueItem(long)).

Also, add support for the **Now playing** icon, which is an indicator for what
is playing. To do this, call the [`setActiveQueueItemId`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat.Builder#setActiveQueueItemId(long)) method and pass
the ID of the playing item in the queue. You need to update
`setActiveQueueItemId` whenever there is a queue change.
| **Note:** Adding support for a **Now playing** icon is only required when supporting a play queue.

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`](https://developer.android.com/reference/android/support/v4/media/session/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`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat#setExtras(android.os.Bundle)) method with an extras bundle that contains
constants that correspond to the reserved functions.
[`SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT`](https://developer.android.com/reference/androidx/media/utils/MediaConstants#SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_NEXT())
corresponds to `ACTION_SKIP_TO_NEXT`, and
[`SESSION_EXTRAS_KEY_SLOT_RESERVATION_SKIP_TO_PREV`](https://developer.android.com/reference/androidx/media/utils/MediaConstants#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`](https://developer.android.com/reference/android/support/v4/media/session/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`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#STATE_STOPPED()), [`STATE_PAUSED`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#STATE_PAUSED()),
[`STATE_NONE`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#STATE_NONE()), or [`STATE_ERROR`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat#STATE_ERROR()).
| **Note:** When `PlaybackStateCompat` is set to `STATE_NONE`, Android Auto and AAOS make playback UI inaccessible. Only send this state when the app has nothing to play and the user needs to select new content to initiate playback.

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](https://developer.android.com/training/cars/media/enable-playback#reserve-space) (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](https://developer.android.com/training/cars/media/enable-playback#required-actions).
Don't use them to replace or duplicate standard actions.

To add custom actions, use the [`addCustomAction`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat.Builder#addCustomAction(android.support.v4.media.session.PlaybackStateCompat.CustomAction)) method in the
[`PlaybackStateCompat.Builder`](https://developer.android.com/reference/android/support/v4/media/session/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`](https://github.com/googlesamples/android-UniversalMusicPlayer/blob/f3154af7ac972ee9b7b1fd32ca3c935e02268a18/mobile/src/main/java/com/example/android/uamp/playback/PlaybackManager.java#L150-L171)
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`](https://developer.android.com/reference/android/support/v4/media/session/MediaSessionCompat.Callback#onCustomAction(java.lang.String,%20android.os.Bundle)) 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`](https://github.com/googlesamples/android-UniversalMusicPlayer/blob/f3154af7ac972ee9b7b1fd32ca3c935e02268a18/mobile/src/main/java/com/example/android/uamp/playback/PlaybackManager.java#L328-L346) 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_`](https://developer.android.com/reference/kotlin/androidx/media3/session/CommandButton#ICON_ALBUM())
constants, set the integer value for the
[`EXTRAS_KEY_COMMAND_BUTTON_ICON_COMPAT`](https://developer.android.com/reference/androidx/media3/session/MediaConstants#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`](https://developer.android.com/reference/android/support/v4/media/session/PlaybackStateCompat.CustomAction.Builder#Builder(java.lang.String,java.lang.CharSequence,int)), 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](https://developer.android.com/guide/topics/graphics/vector-drawable-resources). 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.](https://developer.android.com/static/images/training/icon_off.png) 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`](https://developer.android.com/reference/androidx/car/app/mediaextensions/MetadataExtras#KEY_CONTENT_FORMAT_TINTABLE_LARGE_ICON_URI()) and the
[`KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI`](https://developer.android.com/reference/androidx/car/app/mediaextensions/MetadataExtras#KEY_CONTENT_FORMAT_TINTABLE_SMALL_ICON_URI()) in the extras bundle of the
currently playing media item (passed to [`MediaSession.setMetadata`](https://developer.android.com/reference/android/media/session/MediaSession#setMetadata(android.media.MediaMetadata))).
Set both extras to accommodate the different layouts.

In addition, you can set the [`KEY_IMMERSIVE_AUDIO`](https://developer.android.com/reference/androidx/car/app/mediaextensions/MetadataExtras#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.

## Add links from currently-playing item

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`](https://developer.android.com/reference/androidx/car/app/mediaextensions/MetadataExtras#KEY_SUBTITLE_LINK_MEDIA_ID()) metadata
(to link from the subtitle) or [`KEY_DESCRIPTION_LINK_MEDIA_ID`](https://developer.android.com/reference/androidx/car/app/mediaextensions/MetadataExtras#KEY_DESCRIPTION_LINK_MEDIA_ID()) (to link
from the description). For details, see the reference documentation for those
metadata fields.