Since 1.9.0, skip buttons in SLOT_BACK / SLOT_FORWARD reach legacy controllers as custom actions
Maintainers usually reply within 5 days
@marcbaechinger is already working on this.
Since Oct 28, 2026.
Assessment
This issue has not been assessed yet.
Description
Version
Media3 1.11.1
More version details
The behaviour starts in 1.9.0 and is the same on main. 1.8.x does not have it.
The session sets media button preferences with setMediaButtonPreferences. They include a CommandButton with player command COMMAND_SEEK_TO_PREVIOUS in SLOT_BACK and one with COMMAND_SEEK_TO_NEXT in SLOT_FORWARD.
1.9.0 and later. Commit 8b334e45a9 "Use standard commands in custom layout conversion" (in 1.9.0 as cherry-pick 0527f5a2de) makes this intentional. The 1.9.0 release note:
Add backwards-compatibility for
CommandButtoninstances usingPlayer.Commandor non-customSessionCommandto define their action. These are now correctly represented in platform media sessions, for example for System UI or Android Auto.
CommandButton.getCustomLayoutFromMediaButtonPreferences now accepts any button that can execute its action, and converts it with convertToPredefinedCustomCommandButton. That method names it after the player command (CommandButton.java#L1635-L1637):
} else {
customCommandName = CUSTOM_COMMAND_PLAYER_COMMAND_PREFIX + playerCommand;
}
with (CommandButton.java#L506-L507):
private static final String CUSTOM_COMMAND_PLAYER_COMMAND_PREFIX =
"androidx.media3.session.PLAYER_COMMAND_";
MediaSessionLegacyStub.createPlaybackStateCompat then removes the standard skip action for a filled slot (MediaSessionLegacyStub.java#L1923-L1930):
if (!mediaButtonPreferences.isEmpty()
&& CommandButton.containsButtonForSlot(customLayout, CommandButton.SLOT_BACK)) {
actions &= ~PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
}
if (!mediaButtonPreferences.isEmpty()
&& CommandButton.containsButtonForSlot(customLayout, CommandButton.SLOT_FORWARD)) {
actions &= ~PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
}
So the legacy session publishes custom actions androidx.media3.session.PLAYER_COMMAND_7 and androidx.media3.session.PLAYER_COMMAND_9, in that order (Previous, then Next). It does not publish ACTION_SKIP_TO_PREVIOUS or ACTION_SKIP_TO_NEXT.
1.8.x. getCustomLayoutFromMediaButtonPreferences skips a button without a custom session command (CommandButton.java#L1592-L1597):
CommandButton button = mediaButtonPreferences.get(i);
if (!button.isEnabled
|| button.sessionCommand == null
|| button.sessionCommand.commandCode != COMMAND_CODE_CUSTOM) {
continue;
}
The skip buttons do not fill a slot, so the session keeps the standard skip actions. The same app code gives a correct car layout on 1.8.x.
The Media3 guide for mobile surfaces documents the new result: when the media button preferences contain a button for SLOT_BACK, that slot shows "Custom".
Devices that reproduce the issue
- Android Automotive OS emulator,
android-35-ext15automotive Play Store image, car media app - Android Auto (Desktop Head Unit)
- Phone system media controls
Devices that do not reproduce the issue
None known.
Reproducible in the demo app?
Not tested.
Reproduction steps
- In a
MediaLibraryService, callsetMediaButtonPreferenceswith:- a
CommandButtonwithPlayer.COMMAND_SEEK_TO_PREVIOUS,ICON_PREVIOUS, slotSLOT_BACK - a
CommandButtonwithPlayer.COMMAND_SEEK_TO_NEXT,ICON_NEXT, slotSLOT_FORWARD
- a
- Give the playing
MediaItema heart rating (MediaMetadata.Builder.setUserRating(HeartRating(false))), as an app with favorites does. - Start playback.
- Read
MediaControllerCompat.getPlaybackState()from a legacy controller. Check the actions and the custom actions. - Open the playback view in the Android Automotive OS car media app.
Expected result
A button whose command is the standard skip command, in its own slot, is published as ACTION_SKIP_TO_PREVIOUS / ACTION_SKIP_TO_NEXT, as before 1.9.0. It is not published as a custom action.
Actual result
The playback state has no skip actions. It has custom actions PLAYER_COMMAND_7 and PLAYER_COMMAND_9, in that order.
On Android Automotive OS the order on screen is wrong. The cause is in the AOSP car media app (packages/apps/Car/libs, car-media-common), not in Media3's order:
PlaybackViewModel.getCustomActionsputs the rating action first, for aRATING_HEARTsession, then the session's custom actions.MediaButtonController.updateCustomActionsfills slots by list position. With no standard skip actions, the first custom action goes in the left slot, the second in the right slot, and the rest outside.
With the heart rating from step 2, the bar on the emulator reads:
Next, rating star, play/pause, Previous
Without a rating, Previous and Next land left and right of play/pause. The order then looks right, but the buttons are still custom actions.
On phone system media controls and Android Auto, the layout is right. The only difference is the icons: they draw Media3's icons, not the surface's own skip icons.
Workaround: leave the skip buttons out of setMediaButtonPreferences. The session then keeps the standard skip actions, and each surface draws its own skip buttons. This works on Android Auto (DHU), Android 13 and 16 phone media controls, the Android 12L notification, and the Android Automotive OS emulator. The slots are not always reserved: the seek bar workaround in MediaSessionLegacyStub (needsButtonReservationWorkaroundForSeekbar) never reserves the next slot.
The mobile surfaces table shows Previous and Next when the back and forward slots are empty, so apps can leave skip out. But placing skip in its own slot is a natural reading of the slot API, and since 1.9.0 it gives a wrong layout on car UIs.
Request: for a skip button in its own slot, please publish the standard skip action, as in 1.8.x. If that is not possible, please document that apps should leave skip out of the media button preferences.
Media
Not applicable — no specific media needed.
Bug Report
Will provide on request.
- Dominant language
- Java
- Stars
- 3.1k
- Forks
- 1k
- Avg merge
- 8d 4h
- Merged PRs (30d)
- 5
Getting set up
- No Dockerfile or Docker Compose file
- No pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from androidx/media
-
Difficulty 3/5 1-2 days Newbie friendliness 42/100
Maintainers usually reply within 5 days
-
Difficulty 4/5 3-5 days Newbie friendliness 22/100
Maintainers usually reply within 5 days
-
Cast: system output switcher on Samsung One UI 9 (Android 17) only offers Samsung Audio Mirroring, never a Cast sessionPossibly taken @tanay187 claimed this 2 days ago. Openbug cast needs triage
androidx/media#3500 · 3 comments · 2 assignees ·
Maintainers usually reply within 5 days
-
Waive ads when seek/start at the beginningPossibly taken @microkatz claimed this 4 days ago. Openenhancement
androidx/media#3496 · 3 comments · 1 assignee ·
Maintainers usually reply within 5 days
-
Allow configuring asset-list prefetch timing per HLS interstitialPossibly taken @marcbaechinger claimed this 5 days ago. Openenhancement
androidx/media#3494 · 3 comments · 1 assignee ·
Maintainers usually reply within 5 days
Similar issues
-
[Bug] 资源包列表 与 模组/数据包/世界 列表排列顺序不一致Possibly taken @fishdry1000 claimed this 4 days ago. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 82/100
AtlasOfLivingAustralia/species-lists#882 ·
Maintainers usually reply within 1 day
-
Type/Bug
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
ballerina-platform/ballerina-library#9458 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
winder/Universal-G-Code-Sender#3242 ·
Maintainers usually reply within 1 day
-
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
PCL-Community/PCL-CE#3767 ·
Maintainers usually reply within 1 day