Skip to main content

Interface: RemotePlayback

Defined in: packages/media-session/src/types.ts:99

"Playback is coming out of another device right now, and here is that device's volume."

Published with MediaServiceApi.setRemotePlayback while some remote backend owns the audio — a Cast receiver, a UPnP renderer, a multi-room protocol — and cleared when the phone takes it back. Nothing here is cast-specific: this package works with any player and any output.

Properties

holdLocalAudioSlot?

optional holdLocalAudioSlot?: boolean;

Defined in: packages/media-session/src/types.ts:171

Android only: hold a silent local audio output for as long as this remote playback is published, so the app keeps the platform's "last played locally" slot.

Default

false

What it is for

With the screen off, MediaSessionService discards the session it just chose — and drops the key entirely — when the caller's uid was the last to play local audio (b/275185436). The caller with the screen off is PhoneWindowManager, uid 1000, so any system sound (a notification, a ringtone) takes the volume keys away from a remote session. It is sticky, not momentary: the platform's list never evicts its head entry, and an app whose audio is remote never plays locally to displace it.

The one documented escape: when the head uid goes inactive, the platform promotes the first still-active uid to the head. An app holding a silent local output is that uid, so it reclaims the slot as soon as the interfering sound ends. See the package README, "Two platform conditions".

What it costs, and why it is off by default

A real audio output stays open for the whole remote session, which keeps the audio HAL awake — measurable battery, for a feature the user only notices when they reach for the rocker. It also makes AudioSystem.isStreamActive(STREAM_MUSIC) true, which changes the failure mode rather than only removing one: if the session is ever discarded for the other reason (playback not PLAYING), the key now moves the phone's volume instead of doing nothing.

So it is opt-in. Turn it on when lock-screen volume over a remote device matters more than idle power — a cast-heavy music app — and leave it off otherwise. It takes no audio focus and does not touch your player.

No-op on iOS, where the hardware buttons cannot be taken over at all.


muted?

optional muted?: boolean;

Defined in: packages/media-session/src/types.ts:109

Whether the remote device is muted.

Default

false

routingControllerId?

optional routingControllerId?: string;

Defined in: packages/media-session/src/types.ts:136

Android only: the MediaRouter2 routing-controller id of the route the audio is on, when the backend exposes one.

Lets the system output switcher tie the volume slider it draws to the route that is playing. A refinement — omit it and everything else still works.


steps?

optional steps?: number;

Defined in: packages/media-session/src/types.ts:119

How many notches a hardware volume key press moves through, silent to full.

Default

20

20 is media3's own choice for a Cast receiver (RemoteCastPlayer.MAX_VOLUME, media3 1.11.0), so an app that says nothing gets the step size Android users already feel from every other cast-enabled app.


volume

volume: number;

Defined in: packages/media-session/src/types.ts:107

The remote device's current volume, 0..1.

The same scale an app's own slider uses, and the one every remote backend already speaks. Quantising it into the platform's integer notches is this package's job — see steps.


volumeControl?

optional volumeControl?: RemoteVolumeControl;

Defined in: packages/media-session/src/types.ts:128

How much of that volume the backend can actually drive.

Default

'absolute'

'absolute' (a level can be set and nudged) is what a Cast receiver, a UPnP renderer and essentially every network audio target support, so it is the default. 'fixed' deliberately leaves the hardware keys dead — the honest state for a device whose volume the sender may not touch.