Interface: RnMediaMediaSession
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1453
The process-wide media session.
A singleton by nature, not by convenience: MediaSessionService and
MPNowPlayingInfoCenter are both process-singular (CLAUDE.md principle 5's
documented exception).
Named initialize rather than init because init is a Swift keyword and
nitrogen emits the method name verbatim into the generated Swift protocol.
Extends
HybridObject<{android:"kotlin";ios:"swift"; }>
Properties
name
readonly name: string;
Defined in: node_modules/react-native-nitro-modules/lib/typescript/HybridObject.d.ts:52
The HybridObject's name.
Inherited from
HybridObject.name
Methods
cancelSleepTimer()
cancelSleepTimer(): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1635
Disarm the sleep timer. A no-op when none is armed.
Returns
void
dispose()
dispose(): void;
Defined in: node_modules/react-native-nitro-modules/lib/typescript/HybridObject.d.ts:94
Disposes any resources this HybridObject might hold natively,
and releases this HybridObject's NativeState.
After calling (), this object can no longer be used.
Eagerly disposing a HybridObject could be beneficial for a queue-/handler-architecture
where a bunch of Hybrid Objects are allocated, and later deallocated once a callback (e.g. a render function)
completes.
Returns
void
Note
It is NOT required to call () manually, as the JavaScript
Garbage Collector automatically disposes and releases any resources when needed.
It is purely optional to eagerly-, and manually-, call () here - use with caution!
Inherited from
HybridObject.dispose
equals()
equals(other: HybridObject<{
android: "kotlin";
ios: "swift";
}>): boolean;
Defined in: node_modules/react-native-nitro-modules/lib/typescript/HybridObject.d.ts:79
Returns whether this HybridObject is the same object as other.
While two HybridObjects might not be equal when compared with ==, they might still
hold the same underlying HybridObject, in which case equals(other)
will return true.
Parameters
| Parameter | Type |
|---|---|
other | HybridObject<{ android: "kotlin"; ios: "swift"; }> |
Returns
boolean
Example
const hybridA = SomeModule.getExistingHybridInstance()
const hybridB = SomeModule.getExistingHybridInstance()
console.log(hybridA.equals(hybridB)) // true
Inherited from
HybridObject.equals
getCarConnection()
getCarConnection(): string;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1698
Is a car connected right now — 'none' | 'androidAuto' | 'automotiveOs' | 'carPlay'.
Synchronous and cheap (a field read). Android derives it from the set of
connected controllers media3 classifies as the Auto companion
(com.google.android.projection.gearhead) or Automotive OS
(com.android.car.media, com.android.car.carlauncher); iOS from whether
a CPTemplateApplicationScene is connected. The reactive twin is the
useCarConnection() hook, fed by
MediaSessionHandlers.onCarConnectionChanged.
Returns
string
getSleepTimer()
getSleepTimer(): NativeSleepTimerState | undefined;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1657
The armed timer's mode and — when knowable — its remaining seconds, or
undefined when none is armed.
The structured form of getSleepTimerRemaining, which cannot describe
a trackEnd timer whose deadline is not yet computable. Same clock, same
synchronous cheapness.
Returns
NativeSleepTimerState | undefined
getSleepTimerRemaining()
getSleepTimerRemaining(): number | undefined;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1647
Seconds until the armed timer fires, or undefined when none is armed.
Read from the same clock the timer was scheduled against —
SystemClock.uptimeMillis() on Android (what Handler.postDelayed uses)
and DispatchTime.now() on iOS — so it can never disagree with when the
pause will actually happen, and a wall-clock change cannot move it.
Synchronous and cheap: it is meant to be polled by a UI that is on screen,
which is the one place a JS timer does work.
Returns
number | undefined
initialize()
initialize(config: MediaSessionConfig, handlers: MediaSessionHandlers): Promise<void>;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1468
Install the handlers and platform configuration.
Android: creates the notification channel and the MediaLibrarySession
(bound to a facade SimpleBasePlayer). It does not start the
foreground service — that happens on the first play while the app is
startable, per the Android 12+ background-FGS-start restriction.
iOS: installs MPRemoteCommandCenter targets.
Rejects when called twice without an intervening stopService.
Parameters
| Parameter | Type |
|---|---|
config | MediaSessionConfig |
handlers | MediaSessionHandlers |
Returns
Promise<void>
invalidateBrowse()
invalidateBrowse(parentId?: string): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1684
The children of parentId changed — a download finished, a library synced,
the user signed in.
Evicts the native browse cache entry and tells every connected browser:
Android calls MediaLibrarySession.notifyChildrenChanged, iOS re-fetches
and re-sections any visible template showing that parent. Omit parentId
to mean "everything", which evicts the whole cache and notifies for every
parent that was in it plus the root.
Parameters
| Parameter | Type |
|---|---|
parentId? | string |
Returns
void
setBrowseCapabilities()
setBrowseCapabilities(caps: NativeBrowseCapabilities): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1672
Declare which browse capabilities the app's handler actually implements.
Called by the TS layer at initialize and whenever the handler is
replaced. Android turns search: false into the removal of
COMMAND_CODE_LIBRARY_SEARCH from the session commands granted to
browsers, which is the only thing that makes media3's legacy stub
advertise android.media.browse.SEARCH_SUPPORTED = false — Android Auto
hides its search tab from that key alone. iOS has no search surface, so
this is recorded and otherwise unused there.
Parameters
| Parameter | Type |
|---|---|
caps | NativeBrowseCapabilities |
Returns
void
setMediaItem()
setMediaItem(item?: NativeMediaItem): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1477
Broadcast channel 2 of 3. Pass nothing to clear the metadata.
Parameters
| Parameter | Type |
|---|---|
item? | NativeMediaItem |
Returns
void
setPlaybackState()
setPlaybackState(state: NativePlaybackState): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1474
Broadcast channel 1 of 3. See NativePlaybackState.
Parameters
| Parameter | Type |
|---|---|
state | NativePlaybackState |
Returns
void
setQueue()
setQueue(items: NativeMediaItem[]): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1480
Broadcast channel 3 of 3. Pass [] to clear the queue.
Parameters
| Parameter | Type |
|---|---|
items | NativeMediaItem[] |
Returns
void
setRemotePlayback()
setRemotePlayback(remote?: NativeRemotePlayback): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1554
Declare that playback is currently coming out of another device, and carry that device's volume — or pass nothing to say it is local again.
Not a fourth broadcast channel: it says nothing about what is playing. It
is a routing statement, in the same family as
setResumptionSnapshot — the session needs it, no surface renders
the app's playback state from it, and it is sticky (a setPlaybackState
does not clear it) because "the audio is on the speaker" is a mode, not a
per-broadcast fact.
Android: this is what makes hardware volume keys reach the speaker
The facade Player starts reporting DeviceInfo.PLAYBACK_TYPE_REMOTE
with the published range, and advertises COMMAND_GET_DEVICE_VOLUME plus
the set/adjust commands implied by
NativeRemotePlayback.volumeControl. media3's
MediaSessionLegacyStub reacts to the DeviceInfo change by calling
MediaSessionCompat.setPlaybackToRemote(volumeProvider) on the platform
session — and the platform's own contract for that call is the whole
feature:
Configure this session to use remote volume handling. This must be called to receive volume button events, otherwise the system will adjust the appropriate stream volume for this session. —
android.media.session.MediaSession.setPlaybackToRemote
Because the routing lives on the session, it works with the app
backgrounded and the screen locked, which is exactly where an Activity's
dispatchKeyEvent cannot help. Presses arrive at
MediaSessionHandlers.increaseDeviceVolume /
MediaSessionHandlers.decreaseDeviceVolume.
Passing nothing restores local handling: media3 sees the DeviceInfo go
back to PLAYBACK_TYPE_LOCAL and calls setPlaybackToLocal, after which
the keys move the phone's music stream again. There is no state left
behind — the transition is driven by the same value both ways.
iOS: honestly, nothing
A no-op, and not for lack of trying: iOS gives an app no way to take over
the hardware volume buttons. MPVolumeView renders the system volume
slider, AVAudioSession.outputVolume is read-only, and Google's own
GCKUICastContainerViewController.physicalVolumeButtonsWillControlDeviceVolume
is documented as having no effect since iOS 15. The buttons therefore keep
controlling the phone. Calling this on iOS is harmless and free — the same
app code runs on both platforms — it simply changes nothing there.
Parameters
| Parameter | Type |
|---|---|
remote? | NativeRemotePlayback |
Returns
void
setResumptionSnapshot()
setResumptionSnapshot(snapshot?: string): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1506
Mirror the persisted session into native-owned storage, for Android playback resumption.
Not a fourth broadcast channel and not an app-facing call: withPersistence
invokes this with the very same JSON record it hands the app's storage
engine, so the two copies cannot drift. Pass undefined to forget it.
Why a native mirror exists at all
The whole point of playback resumption is that it works with no
JavaScript in the process. The service is created by the OS, has ~5 s to
call startForeground, and must already know what track to show. Reading
the app's storage engine is not an option: that engine is JavaScript, and
JavaScript is precisely what is missing. So the record is also written to
SharedPreferences owned by this package — survives process death, read
back synchronously on the service's main thread, no bridge involved
(ARCHITECTURE §9, native-first).
The JS-side storage stays the app-facing source of truth; this is a cache that only the resumption path reads. Ignored on iOS, which has no service to resurrect.
Parameters
| Parameter | Type | Description |
|---|---|---|
snapshot? | string | the serialized PersistedSession record, or undefined. |
Returns
void
setSleepTimer()
setSleepTimer(seconds: number): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1601
Pause playback in seconds. Replaces any timer already armed.
Why this is native and not setTimeout
A JS sleep timer is broken by construction on Android: RN's
JavaTimerManager gates timers on the Activity lifecycle plus headless
tasks, so with no Activity they simply stop firing — and Samsung freezes
them even with one (RN #56324). A sleep timer is the one feature whose
entire job happens after the user has put the phone down, i.e. exactly when
JS timers do not run. So it lives on a platform timer: a main-looper
Handler.postDelayed on Android, a DispatchQueue.main.asyncAfter work
item on iOS. Neither is tied to an Activity, and neither is a JS timer.
What happens when it fires
- Playback is paused natively, on the identical path a notification
pause takes — the facade
Playeris paused, so the session, the notification and the lock screen all go topausedimmediately, and the app'spausehandler is invoked to actually stop the audio. - MediaSessionHandlers.onSleepTimer is invoked fire-and-forget.
The timer is armed even with no Activity alive; it does not survive process death (nothing does — see the README's background-playback limits) and it is cancelled by stopService and by a dev reload.
iOS note, stated honestly: iOS suspends a backgrounded process shortly after audio stops, and a suspended process runs no timers. That is not a problem for this feature — while audio is playing the process is not suspended, so a timer armed during playback fires. What cannot be relied on is a timer armed (or still pending) while nothing is playing.
Parameters
| Parameter | Type | Description |
|---|---|---|
seconds | number | strictly positive; the TS layer rejects 0, negatives and non-finite values before they reach here. |
Returns
void
setSleepTimerToTrackEnd()
setSleepTimerToTrackEnd(): void;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1632
Pause when the current item finishes. Replaces any timer already armed.
The mode most sleep-timer users actually want, and the one a JS timer cannot express even in the foreground: "30 minutes" cuts a track in half, "end of this track" does not.
How a package with no playback engine knows when a track ends
From the three broadcast channels, which is all it ever knows — and that is
enough, because they already carry the two numbers involved: the position
anchor ({value, at, rate}) and the current item's duration. The deadline
is (duration - projectedPosition) / rate, computed natively and re-armed
on every broadcast, so a seek, a pause, a rate change or a duration that
arrives late all move it. No polling, no timer on the JS side, nothing
streamed across the bridge — the existing discontinuity-only contract is
exactly the update rate this needs.
Two cases it handles without a duration at all:
- The current item changes (the track ended and the app advanced, or the user skipped) — the timer fires immediately. That is the honest reading of "stop after this one", and it is what makes the feature work on a live stream or before a duration is known.
- The app never broadcasts a duration — armed with no deadline, waiting
for the item change above. getSleepTimer reports
trackEndwith noremainingSecondsrather than pretending.
When it fires the behaviour is identical to setSleepTimer: playback is paused natively first, then MediaSessionHandlers.onSleepTimer.
Returns
void
stopService()
stopService(): Promise<void>;
Defined in: packages/media-session/src/specs/media-session.nitro.ts:1564
End background execution.
Android: releases the session and stops the foreground service. This is the
ONLY thing that does — pause() never does (PLAN §5.4).
iOS: clears the now-playing info and disables every remote command; the
process then lives or dies on whether audio is still playing.
Returns
Promise<void>
toString()
toString(): string;
Defined in: node_modules/react-native-nitro-modules/lib/typescript/HybridObject.d.ts:64
Returns a string representation of the given HybridObject.
Unless overridden by the HybridObject, this will return the name of the object.
Returns
string
Example
const hybridA = SomeModule.getExistingHybridInstance()
console.log(hybridA.toString()) // [HybridObject HybridA]
Inherited from
HybridObject.toString