The car server app runs on the BYD DiLink infotainment system. It uses a parallel connection model with WiFi (3 dedicated connections) and USB tracks running simultaneously:
checkAndAdvance() evaluates state when any prerequisite changesStates: IDLE → CONNECTING → CONNECTED → STREAMING
Car APK is embedded in the phone APK. The phone auto-updates the car app via dadb when version mismatch is detected during handshake. Car receives UPDATING_CAR message and shows status instead of reconnecting blindly.
The car app separates the connection flow from the streaming experience into two distinct modes, matching the phone app’s approach:
Launch mode (CarLaunchScreen): Full-screen, connection-focused. Shows branding, step-by-step connection instructions, connection status, and manual IP entry. No navigation bar — the entire screen is dedicated to getting connected. Shown when the car app starts and remains until app icons are received from the phone via the control connection.
Streaming mode (CarShell with PersistentNavBar): The familiar layout with left navigation bar (notifications, home, back, recent apps) and content area (app grid, mirror view, notifications). Shown once appList is non-empty and the state is CONNECTED or STREAMING.
The transition trigger: when the phone sends APP_LIST via the control connection and the connection state reaches CONNECTED/STREAMING, the UI switches from launch mode to streaming mode.
Foreground service managing the full connection lifecycle with a parallel prerequisite state machine and 3 dedicated connections.
3-Connection Architecture:
controlConnection: handshake, heartbeat, app commands, DATA channel (app list, notifications, car logs)videoConnection: H.264 video frames only (phone → car)inputConnection: touch events only (car → phone)Parallel Track Architecture:
connectionScope: parent Job for all discovery coroutines, cancelled on disconnectcheckAndAdvance() evaluates overall state when any prerequisite changesTrack A — WiFi:
wifiReady = true after all 3 establishedsendTouchEvent() / sendTouchBatch() via input connectionTrack B — USB:
BroadcastReceiver for USB_DEVICE_ATTACHED / USB_DEVICE_DETACHEDMainActivity forwards USB intents to the servicelogSink routing all ADB auth logs to phone’s FileLogam start -n com.dilinkauto.client/.MainActivityUpdate Flow:
UPDATING_CAR, car sets updatingFromPhone = true, shows “Updating car app…” statuspm install -r, car app restarts freshState Flows:
_state, _phoneName, _appList, _notifications, _mediaMetadata, _playbackState: primary state exposed to UI_videoReady: true when first non-config video frame arrives_statusMessage: human-readable status for UI display_vdStackEmpty (SharedFlow): emitted when phone reports VD has no activities (triggers navigation to home)VD Server Launch:
deployVdServer(): shellNoWait with CLASSPATH from handshake’s vdServerJarPathW H DPI PORT EW EH FPS — FPS passed from handshake’s targetFpsEarly Decoder Start:
onSurfaceTextureAvailable stops and restarts decoder with real TextureView surfaceCar Log Routing:
carLogSend() routes all car-side logs through DATA channel CAR_LOG to phonevideoDecoder.logSink and adb.setLogSink() wire VideoDecoder and UsbAdbConnection logs through the same path/sdcard/DiLinkAuto/client.logH.264 decoder using MediaCodec with Surface output (GPU-direct rendering).
onFrameReceived(): queues frames even before start() is calledstart(): feeds cached CONFIG first, then drains queueKEY_LOW_LATENCY = 1, KEY_PRIORITY = 0 for minimum decode delayisRunning property for early start coordinationlogSink callback routes all decoder logs to phone via carLogSendApplication class. Creates notification channel dilinkauto_car_service with IMPORTANCE_LOW.
Direct ADB client using dadb library. Provides tap, swipe, back, home, and app launch via shell commands on the virtual display. Used as an alternative input path.
Full-screen connection-focused composable shown before the phone connection is established — no nav bar, no app grid.
appList becomes non-empty and state reaches CONNECTED/STREAMING76dp left navigation bar — only shown in streaming mode — with:
Width computed to guarantee even viewport for H.264 encoder.
Shown as the main content area when streaming mode is active and current screen is HOME:
imePadding() — keyboard doesn’t push activity, only search bar moveswindowSoftInputMode="adjustNothing" in manifestFull integrated launcher layout with CarStatusBar, SideNavBar (80dp), and AppGrid. Not used in the current CarShell routing — the active UI uses PersistentNavBar + HomeContent/MirrorContent/NotificationContent composables inline.
Tracks recently launched apps (max 5), persisted to SharedPreferences. pruneUnavailable() removes apps no longer present when app list updates.
Individual nav bar widget composables: ClockDisplay (updates every 1s), NetworkInfo (connected/disconnected state), RecentAppIcon (40dp, with active state highlight), NavActionButton (40dp icons, 12sp labels).
Material3 dark color scheme (CarDark) with category-specific app tile colors: Navigation (green), Music (pink), Communication (blue), Other (gray).
Tested on BYD DiLink 3.0: