How Clipboard Synchronization Works in Scrcpy: Android-to-Host Deep Dive

Scrcpy synchronizes clipboards by registering an Android clipboard listener that pushes device changes to the host via DeviceMessage objects, while handling host-to-device updates through control messages that set the Android clipboard via a ServiceManager wrapper.

Clipboard synchronization in Genymobile/scrcpy enables seamless copy-paste between your Android device and computer. This feature operates through a multi-layered architecture on the server side, utilizing Android's ClipboardManager system service wrapped in custom abstractions. The implementation ensures bidirectional sync while preventing feedback loops and gracefully handling devices without clipboard services.

Architecture Overview

The synchronization mechanism relies on a control thread running on the Android device that communicates with the desktop client over a socket connection. The architecture separates concerns into configuration, service wrapping, device abstraction, and control logic layers.

Key components coordinate through the following flow:

  • Options stores the clipboardAutosync flag
  • ServiceManager lazily initializes the clipboard service wrapper
  • Device provides static helper methods for read/write operations
  • Controller manages the event loop and message protocol

Server-Side Implementation

Configuration via Options.java

User control over clipboard behavior starts in server/src/main/java/com/genymobile/scrcpy/Options.java. The class defines a boolean field clipboardAutosync that defaults to true, enabling automatic synchronization from device to host whenever the Android clipboard changes.

Users can disable this feature via the command line:

scrcpy --no-clipboard-autosync

When disabled, the controller skips listener registration and relies on manual clipboard retrieval triggered by COPY or CUT key events.

ServiceManager and ClipboardManager Wrapper

Android's clipboard service is accessed through server/src/main/java/com/genymobile/scrcpy/wrappers/ServiceManager.java. The getClipboardManager() method lazily creates a wrapper around android.content.ClipboardManager:

// From ServiceManager.java
public ClipboardManager getClipboardManager() {
    if (clipboardManager == null) {
        clipboardManager = ClipboardManager.create();
    }
    return clipboardManager;
}

The wrapper class in server/src/main/java/com/genymobile/scrcpy/wrappers/ClipboardManager.java provides simplified methods:

  • getText() – Retrieves the current primary clip as a string
  • setText(String text) – Sets the primary clip content
  • addPrimaryClipChangedListener() – Registers callbacks for clipboard changes

Device.java Static Helpers

High-level clipboard operations are abstracted in server/src/main/java/com/genymobile/scrcpy/device/Device.java through static helper methods:

Reading the clipboard:

public static String getClipboardText() {
    ClipboardManager clipboardManager = ServiceManager.getClipboardManager();
    if (clipboardManager == null) {
        return null;
    }
    return clipboardManager.getText();
}

Writing with duplicate prevention:

public static boolean setClipboardText(String text) {
    ClipboardManager clipboardManager = ServiceManager.getClipboardManager();
    if (clipboardManager == null) {
        return false;
    }
    String current = clipboardManager.getText();
    if (text.equals(current)) {
        // Already set, do nothing to avoid duplicate notifications
        return false;
    }
    clipboardManager.setText(text);
    return true;
}

The duplicate check prevents unnecessary network traffic and feedback loops when the same text is set multiple times.

The Control Thread and Two-Way Synchronization

The Controller class in server/src/main/java/com/genymobile/scrcpy/control/Controller.java orchestrates the bidirectional synchronization through the control thread.

Device to Host Synchronization

When clipboardAutosync is enabled, the controller registers a primary-clip-changed listener during initialization:

// Simplified flow from Controller.java
if (options.clipboardAutosync) {
    clipboardManager.addPrimaryClipChangedListener(() -> {
        if (!isSettingClipboard.get()) {
            String text = Device.getClipboardText();
            if (text != null) {
                deviceMessageSender.send(DeviceMessage.createClipboard(text));
            }
        }
    });
}

The isSettingClipboard AtomicBoolean prevents the listener from reacting to changes initiated by the controller itself, breaking potential feedback loops.

When autosync is disabled, the controller falls back to manual retrieval via the getClipboard() method, typically triggered after COPY or CUT key events.

Host to Device Synchronization

When the user copies text on the host computer, the client sends a set-clipboard control message to the Android device. The controller handles this in setClipboard():

public void setClipboard(String text, boolean paste, int sequence) {
    isSettingClipboard.set(true);
    try {
        Device.setClipboardText(text);
        if (paste) {
            // Inject paste key event
            injectKeyEvent(KEYCODE_PASTE);
        }
        if (sequence != ControlMessage.SEQUENCE_INVALID) {
            deviceMessageSender.send(DeviceMessage.createAckClipboard(sequence));
        }
    } finally {
        isSettingClipboard.set(false);
    }
}

The method wraps the clipboard operation with the isSettingClipboard flag to suppress the listener callback. If the user initiated a paste action (e.g., Ctrl+V), the controller injects KEYCODE_PASTE after setting the clipboard.

Disabling Automatic Synchronization

Users can disable the automatic device-to-host synchronization while retaining manual clipboard control:

scrcpy --no-clipboard-autosync

With this flag:

  • The primary-clip-changed listener is never registered
  • Clipboard content is only retrieved when explicitly requested (e.g., after COPY/CUT operations)
  • Host-to-device synchronization remains functional

Code Examples

Checking Clipboard Content Programmatically

import com.genymobile.scrcpy.device.Device;

// Read current device clipboard
String content = Device.getClipboardText();
if (content != null) {
    System.out.println("Clipboard contains: " + content);
} else {
    System.out.println("No clipboard manager available");
}

Writing to Device Clipboard

import com.genymobile.scrcpy.device.Device;

// Set text (returns false if text unchanged or manager unavailable)
boolean success = Device.setClipboardText("New clipboard content");
if (success) {
    System.out.println("Clipboard updated successfully");
}

Controller Message Flow

// Device -> Host: Creating a clipboard message
DeviceMessage msg = DeviceMessage.createClipboard("Text copied on device");
deviceMessageSender.send(msg);

// Host -> Device: Handling paste with sequence number
// sequence is used for acknowledgement tracking
controller.setClipboard("Text from host", true, sequence);

Summary

  • Scrcpy implements bidirectional clipboard synchronization through a dedicated control thread running on the Android device.
  • The clipboardAutosync option (default true) controls whether device clipboard changes automatically push to the host via a primary-clip-changed listener.
  • ServiceManager and ClipboardManager wrap Android's system service, providing getText() and setText() abstractions used by Device.java.
  • The Controller class prevents feedback loops using an isSettingClipboard atomic flag when handling host-to-device updates.
  • Users can disable autosync with --no-clipboard-autosync while retaining manual clipboard retrieval via COPY/CUT key events.

Frequently Asked Questions

How do I disable automatic clipboard synchronization in Scrcpy?

Use the command-line flag --no-clipboard-autosync when launching scrcpy. This prevents the controller from registering the primary-clip-changed listener, stopping automatic device-to-host updates while still allowing manual clipboard retrieval when you press COPY or CUT on the device.

Does Scrcpy support clipboard synchronization on all Android devices?

No. If the Android device does not expose the clipboard system service, ServiceManager.getClipboardManager() returns null and both synchronization directions gracefully degrade to no-ops. Scrcpy logs a warning ("No clipboard manager") in this scenario but continues operating normally.

How does Scrcpy prevent infinite loops when synchronizing clipboards?

The Controller class uses an AtomicBoolean named isSettingClipboard as a guard flag. When the controller writes to the device clipboard (host-to-device), it sets this flag to true. The primary-clip-changed listener checks this flag and skips processing when it is true, preventing the controller from reacting to its own changes.

Can I paste text from my computer directly into an Android app using Scrcpy?

Yes. When you copy text on your host computer, scrcpy sends a set-clipboard control message to the Android device. If you trigger a paste action (typically Ctrl+V), the controller not only sets the device clipboard but also injects KEYCODE_PASTE into the focused Android application, completing the paste operation automatically.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →