Skip to content

Clipboard Synchronization

星冉 edited this page Jul 21, 2026 · 1 revision

Clipboard Synchronization

中文

Clipboard content synchronizes bidirectionally over the scrcpy 4.1 control socket, not ADB Shell. Text is transferred as raw UTF-8 so quotes, newlines, Chinese, Emoji, and command characters are never interpreted by a shell.

Controller to target

  • Ctrl+V reads the local clipboard and sends SET_CLIPBOARD(paste=true). The server pastes after setting the target clipboard; the client does not send a delayed KEYCODE_PASTE.
  • One basic ASCII character keeps the INJECT_TEXT path. Non-ASCII, newline/tab, multi-character commits, or content over 300 UTF-8 bytes uses clipboard.
  • Every message enters the ordered control sender. Clipboard is non-droppable, and pending MOVE values are flushed according to the ordering barrier first.

SET_CLIPBOARD wire format:

Field Bytes Value
type 1 9
sequence 8 -1, no ACK requested
paste 1 1 means paste after setting
length 4 UTF-8 byte count, big-endian
text length Raw UTF-8

Length uses UTF-8 bytes, never String.length, and content receives no Shell, JSON, or Base64 escaping.

Target to controller

A separate receiver coroutine continuously reads the control socket:

  • CLIPBOARD: validate length, read exact UTF-8, and write to the local clipboard on the main thread.
  • ACK_CLIPBOARD: fully consume the 8-byte sequence.
  • UHID_OUTPUT: fully consume id, length, and payload so subsequent message boundaries remain aligned.

Sending and receiving cannot share one single-thread loop because blocking reads would stop control writes. Session stop cancels both directions and closes the socket; an old receiver must never read a new session.

Length and memory

The scrcpy control-message limit is 1 << 18 (262,144) bytes:

  • The outgoing fixed header is 14 bytes, leaving 262,130 UTF-8 bytes for text.
  • The incoming clipboard header is 5 bytes, leaving 262,139 UTF-8 bytes for text.

Oversized output fails explicitly instead of truncating a multibyte character. Input validates declared length before allocation. Ordinary messages use a reusable 512-byte buffer; an already encoded large clipboard byte array writes directly to the socket to avoid another large copy.

Large text occupies the ordered control socket while it is written, so later keys and touch barriers wait. Do not split it into interleavable fragments; this message has no scrcpy reassembly semantics.

Security and error boundaries

  • Logs record direction, UTF-8 length, and failure class only, never content.
  • Content is not written to event history, cache, or persistent storage.
  • Android background-clipboard restrictions and paste refusal from protected fields are platform or application security boundaries.
  • An unknown device-message type or invalid length ends the current read instead of interpreting a misaligned stream.
  • Never reintroduce a Shell fallback; it restores escaping, length, and command-injection risks.

Regression anchors

  • ScrcpyClipboardProtocolTest: Chinese, special characters, Emoji/ZWJ, control characters, exact limit, and oversize rejection.
  • ScrcpyDeviceMessageReaderTest: clipboard, ACK, and UHID sequences plus pre-allocation length checks.
  • KeyboardClipboardRoutingTest: ASCII/clipboard routing and the 300-byte INJECT_TEXT boundary.
  • Device coverage: normal fields, WebView, terminal, vendor apps, large text, reverse sync, and protected fields.

On a server upgrade, recheck message types, field order, maximum length, sequence/ACK semantics, and UHID payload, updating protocol tests first.

Clone this wiki locally