Android Auto
The library registers the Media3 browse + playback service in its manifest, so Android Auto support is wired automatically. Your job is to supply the browse tree and resolve play requests.
Android Auto here is phone-projected Auto. Native Android Automotive OS (AAOS) is not supported.
1. Initialize on cold start
Android Auto can launch your app's JavaScript without any UI, so initialize the player and register your handlers from a bootstrap module imported before your app entry — see Setup & manual configuration.
2. Provide a browse tree
There are two complementary ways to feed the browser:
setBrowseSnapshot(snapshot)— push a full, pre-computed tree up front (fast, no per-node round trips).onBrowseRequest(parentId)— resolve a node lazily on drilldown.
import { getTrackPlayer, registerPlaybackService } from 'react-native-queue-player';
// Push a top-level snapshot once you have data.
getTrackPlayer().setBrowseSnapshot({
rootId: 'root',
sections: [
{
id: 'playlists',
title: 'Playlists',
items: [
{ id: 'pl:chill', title: 'Chill', playable: false, hasChildren: true },
{ id: 'pl:focus', title: 'Focus', playable: false, hasChildren: true },
],
},
],
});
Register handlers for lazy browse, search, and playback resolution:
registerPlaybackService(() => ({
// Drill into a node (e.g. open a playlist).
onBrowseRequest: async (parentId) => {
const tracks = await fetchPlaylist(parentId);
return tracks.map((t) => ({
id: t.id, title: t.title, subtitle: t.artist, artworkUrl: t.artworkUrl, playable: true,
}));
},
// Live search box in the car UI.
onSearchRequest: async (query) => {
const results = await search(query);
return results.map((t) => ({ id: t.id, title: t.title, playable: true }));
},
// Resolve a browse item the user tapped into actual tracks to play.
onPlayFromIdRequest: async (mediaId) => {
const tracks = await resolveToTracks(mediaId);
return tracks; // TrackItem[]
},
// Optional connection lifecycle.
onCarConnect: () => {},
onCarDisconnect: () => {},
}));
When a request resolves to TrackItem[], the library sets the queue and starts playback for you.
Connection status
onCarConnect and onCarDisconnect are one-shot events — they fire when the first automotive controller connects and the last one drops. On a cold start the connect can fire before your bootstrap has registered the handler, so don't rely on the event alone.
isCarConnected() is the pull-based counterpart: it returns whether a controller is connected right now, so you can check at any time and gate car-only work — like pushing a fresh browse snapshot — on the current connection.
import { getTrackPlayer } from 'react-native-queue-player';
const player = getTrackPlayer();
if (player.isCarConnected()) {
player.setBrowseSnapshot(await buildSnapshot());
}
It holds the same state the events transition between (the headless Android Auto / Wear / Assistant controllers here; the CarPlay scene on iOS) and is process-scoped — a fresh app process starts disconnected until a controller connects within it.
Service rebirth
onServiceReady(reason) fires when the platform media service is freshly bound
to JS: reason is 'first-bind' on the initial bind of a JS process and
'service-reborn' whenever a different service instance is bound afterwards —
after Android killed the service (memory reclaim, doze, the idle-bucket reaper),
or after your own configure() / destroy() re-bind. A reborn service starts
with an empty browse tree — the snapshot you pushed is gone, and a controller
binding into the new instance sees an empty library until you push it again.
Current limitation: handlers registered with registerPlaybackService are
held by the service instance, not by the player, so a reborn instance has none
of them — 'service-reborn' is not delivered to a handler registered before the
rebirth, and neither are onBrowseRequest or onCarConnect. A service killed
by the OS therefore comes back with an empty library and no signal, and a
'service-reborn' handler cannot repair it. Register again from your app's own
lifecycle (an AppState change, a screen mount) and push the snapshot there if
you need to survive it. The fix is tracked as
#244.
Android-only. iOS has no separate service lifecycle — the app dies whole under
memory pressure — so the handler is accepted for API parity and never invoked
there; onCarConnect is the iOS-side attach signal.
Types
type BrowseSnapshot = { rootId: string; sections: BrowseSection[] };
type BrowseSection = { id: string; title: string; icon?: SectionIcon; artworkUrl?: string; items: BrowseItem[] };
type BrowseItem = { id: string; title: string; subtitle?: string; artworkUrl?: string; playable: boolean; hasChildren?: boolean };
SectionIcon is an exported enum of standard section glyphs (Home, Library, Playlists, Albums, Artists, Songs, RecentlyPlayed, …).
Runtime permission
Request POST_NOTIFICATIONS at runtime (Android 13+) so the media notification appears.
See also: CarPlay (the same handler model on iOS) and Voice (Google Assistant).