# How scrcpy Handles Virtual Displays on Android: Server Architecture Explained

> Discover how scrcpy creates virtual displays on Android using reflection and Surface objects. Understand its server architecture for capturing frames and mapping input events.

- Repository: [Genymobile/scrcpy](https://github.com/Genymobile/scrcpy)
- Tags: internals
- Published: 2026-02-25

---

**Scrcpy creates and manages Android virtual displays by wrapping the hidden DisplayManager APIs via reflection, capturing rendered frames through Surface objects, and coordinating input events with coordinate mapping.**

The Genymobile/scrcpy project streams Android screens to desktops by rendering content into virtual displays. Understanding how scrcpy handles virtual displays on Android reveals the core mechanism behind its screen mirroring and new display creation features. The server-side implementation spans reflection-based system service wrappers, capture pipelines, and input controllers.

## Three-Layer Architecture

Scrcpy's virtual display implementation is organized into three distinct layers:

- **Display Abstraction**: Reflection-based access to Android's hidden APIs via [`DisplayManager.java`](https://github.com/Genymobile/scrcpy/blob/main/DisplayManager.java)
- **Capture Pipeline**: Creation of virtual displays and attachment of Surfaces in [`ScreenCapture.java`](https://github.com/Genymobile/scrcpy/blob/main/ScreenCapture.java) and [`NewDisplayCapture.java`](https://github.com/Genymobile/scrcpy/blob/main/NewDisplayCapture.java)
- **Notification System**: Delivery of display IDs and coordinate mappers through `VirtualDisplayListener` to the `Controller`

## The DisplayManager Wrapper (Reflection Layer)

Since Android hides certain display APIs from the SDK, scrcpy uses reflection to access system-level functionality located in [`server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/wrappers/DisplayManager.java).

The wrapper exposes `createVirtualDisplay` for mirroring existing displays:

```java
public VirtualDisplay createVirtualDisplay(
        String name, int width, int height, int displayIdToMirror, Surface surface)
        throws Exception {
    Method method = getCreateVirtualDisplayMethod();
    return (VirtualDisplay) method.invoke(
            null, name, width, height, displayIdToMirror, surface);
}

```

For creating new logical displays (used with `--new-display`), the wrapper provides `createNewVirtualDisplay` which calls the public API with extended flags at lines 176-181:

```java
virtualDisplay = ServiceManager.getDisplayManager()
        .createNewVirtualDisplay("scrcpy",
                displaySize.getWidth(), displaySize.getHeight(),
                dpi, surface, flags);

```

## Capture Pipeline: Mirroring vs. New Displays

Scrcpy implements two distinct capture strategies depending on whether you mirror an existing display or create a new one.

### Mirroring with ScreenCapture

The `ScreenCapture` class in [`server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/video/ScreenCapture.java) handles mirroring. It first attempts to create a virtual display via the DisplayManager wrapper:

```java
virtualDisplay = ServiceManager.getDisplayManager()
        .createVirtualDisplay("scrcpy", inputSize.getWidth(),
                              inputSize.getHeight(), displayId, surface);

```

If this throws an exception (common on older Android versions or restricted devices), scrcpy falls back to the **SurfaceControl** API to create a secure off-screen display at lines 33-38.

### Creating New Displays with NewDisplayCapture

When users invoke scrcpy with `--new-display`, the `NewDisplayCapture` class in [`server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/video/NewDisplayCapture.java) creates a standalone virtual display with rich feature flags:

```java
int flags = VirtualDisplay.FLAG_PUBLIC
          | VirtualDisplay.FLAG_PRESENTATION
          | VirtualDisplay.FLAG_OWN_CONTENT_ONLY
          | VirtualDisplay.FLAG_SUPPORTS_TOUCH
          | VirtualDisplay.FLAG_ROTATES_WITH_CONTENT;

if (vdDestroyContent) flags |= VirtualDisplay.FLAG_DESTROY_CONTENT_ON_REMOVAL;
if (vdSystemDecorations) flags |= VirtualDisplay.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;

virtualDisplay = ServiceManager.getDisplayManager()
        .createNewVirtualDisplay("scrcpy",
                displaySize.getWidth(), displaySize.getHeight(),
                dpi, surface, flags);

```

## Coordinate Mapping and Input Handling

Creating the virtual display is only half the task; scrcpy must ensure touch and mouse events map correctly to the display coordinates.

### VirtualDisplayListener Interface

Both capture classes implement `VirtualDisplayListener` to notify the system when a display is ready. In [`ScreenCapture.java`](https://github.com/Genymobile/scrcpy/blob/main/ScreenCapture.java) (lines 46-59), successful creation triggers:

```java
vdListener.onNewVirtualDisplay(virtualDisplayId, positionMapper);

```

### Controller Implementation

The `Controller` class in [`server/src/main/java/com/genymobile/scrcpy/control/Controller.java`](https://github.com/Genymobile/scrcpy/blob/main/server/src/main/java/com/genymobile/scrcpy/control/Controller.java) implements this listener, storing the display ID and position mapper for input translation:

```java
public void onNewVirtualDisplay(int virtualDisplayId,
                                 PositionMapper positionMapper) {
    this.virtualDisplayId = virtualDisplayId;
    this.positionMapper = positionMapper;
}

```

The `PositionMapper` converts input event coordinates from the virtual display space back to the original device coordinates, ensuring accurate touch injection.

## Summary

- **scrcpy** creates virtual displays via reflection-based wrappers around Android's hidden DisplayManager APIs, located in [`DisplayManager.java`](https://github.com/Genymobile/scrcpy/blob/main/DisplayManager.java)
- The **ScreenCapture** class mirrors existing displays and falls back to SurfaceControl if the reflection method fails
- **NewDisplayCapture** creates standalone virtual displays with configurable flags when using the `--new-display` option
- The **VirtualDisplayListener** interface bridges the capture pipeline and input controller, delivering display IDs and coordinate mappers
- **Controller** stores the virtual display ID and uses `PositionMapper` to translate input events correctly

## Frequently Asked Questions

### What is the difference between ScreenCapture and NewDisplayCapture in scrcpy?

**ScreenCapture** mirrors an existing physical or logical display on the Android device, while **NewDisplayCapture** creates a brand new virtual display that runs independently. ScreenCapture uses `createVirtualDisplay` with a source display ID, whereas NewDisplayCapture uses `createNewVirtualDisplay` with feature flags like touch support and system decorations.

### Why does scrcpy use reflection to access DisplayManager APIs?

Android hides certain display creation methods from the public SDK for security reasons. Scrcpy uses reflection in [`DisplayManager.java`](https://github.com/Genymobile/scrcpy/blob/main/DisplayManager.java) to access these system-level APIs, allowing it to create virtual displays that can mirror existing screens or capture secure content that standard SDK methods cannot access.

### How does scrcpy handle input events on virtual displays?

Scrcpy implements the `VirtualDisplayListener` interface to receive the virtual display ID and a `PositionMapper` object when a display is created. The `Controller` class stores these and uses them to translate desktop mouse and keyboard coordinates into the correct Android screen coordinates before injecting input events.

### What happens if the virtual display creation fails in ScreenCapture?

If the initial `createVirtualDisplay` call fails (typically due to API restrictions), scrcpy falls back to using the **SurfaceControl** API at lines 33-38 of [`ScreenCapture.java`](https://github.com/Genymobile/scrcpy/blob/main/ScreenCapture.java). This creates a secure off-screen display that can capture content without the standard virtual display limitations, ensuring compatibility across different Android versions.