Skip to content

ADB Connection Lifecycle

星冉 edited this page Aug 4, 2026 · 2 revisions

ADB Connection Lifecycle

中文 · ADB, USB, and Wireless

This page defines responsibilities among the ADB connection pool, primary and streaming lanes, foreground heartbeat, and scrcpy health monitoring. The former model in which scrcpy and management took over a connection based on a delayed-ACK profile is obsolete. A current network AdbConnection manages purpose-specific lanes internally.

Ownership

Component Responsibility
AdbConnectionManager Unified TCP/mDNS/USB APIs, per-device locking, registry, and USB detach
AdbConnectionConnector Create and verify a DadbSession, warm its optional streaming route, and construct AdbConnection
AdbConnectionRegistry Current device-id-to-connection object and connected-device flow
AdbConnection Commands, shell and streams, forwarding, device information, and lane lifecycle
ScrcpyServiceHeartbeatMonitor Check protected transports every 15 seconds and rebuild the same exact identity
ConnectionHealthMonitor Observe current scrcpy video/audio/control sockets without rebuilding the ADB registry

AdbConnectionManager does not run another periodic global keepalive. scrcpy socket health and foreground-service ADB heartbeat observe different data planes.

Connection and reuse

  • TCP and mDNS operations lock by transport device id. An existing object is verified and reused on success; verification failure or forced reconnect closes and replaces it.
  • USB also locks by normalized usb:<serial> and verifies reuse. Permission and system UsbDevice lifetime remain in the USB path.
  • Once a candidate race selects a winner, the session, server, monitoring, and cleanup must hold the same AdbConnection object.
  • Prefer conditional disconnectDeviceIfCurrent during removal so an old heartbeat result cannot close a newly replaced object.

Primary and streaming lanes

The network connector creates a DadbSession with an immediately available primary Dadb using withDelayedAck=false. After primary verification succeeds, it warms one background streaming route whose Dadb uses withDelayedAck=true. Concurrent callers of DadbSession.route(STREAMING) share that creation attempt; a failed attempt can be repeated without closing the primary route. If streaming is unavailable for the session due to routing/handshake/connection failures, the code falls back to PRIMARY and keeps the same connection ownership. Closing the logical session releases both physical connections. USB is exclusive, so its DadbSession has one Dadb and maps both routes to that same transport.

AdbConnection keeps ordinary commands, management shell, file/helper work, and ordinary TCP port forwarding on the primary route. scrcpy server upload/start, localabstract sockets, scrcpy forwarding, and remote encoder detection request the streaming route. Streaming forward state has its own registry so closing or replacing one lane does not lose ownership of the other lane's forwards.

Consequences:

  • Delayed ACK is a lane or negotiated capability, no longer a pair of mutually exclusive “remote” and “management” product profiles.
  • supportsDelayedAck() reports delayed-ACK support only after the streaming route is ready and streaming is not force-fallen back to primary; it must not block to create the route or infer screen ownership.
  • A streaming lane failure that downgrades to PRIMARY disables delayed-ack usage for the current connection while preserving active ordinary lanes and avoiding session teardown.
  • Ordinary and streaming work must call the matching API. A one-shot management command does not create the secondary lane, while scrcpy waits for the shared warmed route when it reaches a streaming operation.

Foreground heartbeat

The foreground service checks only exact registered protected transport identities. If a connection is absent or isConnected() is false, it parses the original device id and rebuilds through the corresponding USB, mDNS, or TCP API. The returned device id must match; otherwise the unexpected connection is closed and the attempt fails.

Devices are checked concurrently within a round, but removal compares both the ProtectedAdbDevice and expected AdbConnection object so an old asynchronous result cannot remove updated state. Persistently failed entries are removed from the protected set.

Boundary with scrcpy

  • ADB heartbeat answers whether the transport remains usable.
  • The scrcpy health monitor answers whether the current media and control sockets remain alive.
  • If ADB is healthy but a socket failed, rebuild the scrcpy path without first destroying the healthy transport.
  • Hard signals such as USB detach clear the registry and publish disconnect events first; heartbeat is only a fallback.
  • Socket closure caused by explicit stop must not be interpreted as a new reconnect request.

Change and regression checks

  • Check device-id locking and conditional removal for connection-replacement races.
  • Network primary verification must complete before the streaming route is warmed; both routes keep the same endpoint and logical-session owner.
  • A protected transport must rebuild with its original TCP, USB, or mDNS identity.
  • Run dadb DadbSessionTest, app ConnectionSocketOrderTest, and ScrcpyServiceHeartbeatMonitorTest, plus device tests for USB detach, network loss/recovery, and delayed-ACK route setup.

Clone this wiki locally