> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/zwc456baby/ScrcpyForAndroid/llms.txt
> Use this file to discover all available pages before exploring further.

# Headless Mode

> Run Scrcpy for Android without user interaction using the START_REMOTE intent extra

## What is Headless Mode?

Headless mode allows Scrcpy for Android to launch and automatically connect to a target device without displaying the main configuration UI. This is ideal for automation, kiosk applications, or scenarios where user interaction should be minimized.

<Info>
  Headless mode is triggered by passing the `START_REMOTE` intent extra when launching the MainActivity.
</Info>

## Enabling Headless Mode

To launch the app in headless mode, include the `START_REMOTE` boolean extra in your intent:

### Via ADB Command

```bash theme={null}
adb shell am start -n com.example.scrcpy/.MainActivity \
  --ez start_remote_headless true
```

### Via Android Intent

```java theme={null}
Intent intent = new Intent(context, MainActivity.class);
intent.putExtra(MainActivity.START_REMOTE, true);
startActivity(intent);
```

### Constant Definition

The constant is defined in `MainActivity.java:61`:

```java theme={null}
public final static String START_REMOTE = "start_remote_headless";
```

## How Headless Mode Works

### Behavior Changes

When headless mode is enabled:

<Steps>
  <Step title="UI Hidden">
    The main scroll view containing connection settings is hidden (`MainActivity.java:213-218`, `273-278`):

    ```java theme={null}
    if (headlessMode) {
        View scrollView = findViewById(R.id.main_scroll_view);
        if (scrollView != null) {
            scrollView.setVisibility(View.INVISIBLE);
        }
    }
    ```
  </Step>

  <Step title="Auto-Connect">
    On first launch, the app automatically attempts to connect using saved preferences (`MainActivity.java:209-212`):

    ```java theme={null}
    if (headlessMode && first_time) {
        getAttributes();
        connectScrcpyServer(PreUtils.get(this, Constant.CONTROL_REMOTE_ADDR, ""));
    }
    ```

    The target device address is loaded from `SharedPreferences` key `CONTROL_REMOTE_ADDR`.
  </Step>

  <Step title="No History Saving">
    Connection history is not saved in headless mode (`MainActivity.java:509-512`):

    ```java theme={null}
    if (headlessMode) {
        // Headless mode does not save history
        return false;
    }
    ```
  </Step>
</Steps>

### State Persistence

The headless mode state is preserved across configuration changes (`MainActivity.java:228`):

```java theme={null}
outState.putBoolean("headlessMode", headlessMode);
```

This ensures that if the activity is recreated (e.g., due to rotation), it remains in headless mode.

## Use Cases for Automation

Headless mode enables various automation scenarios:

<CardGroup cols={2}>
  <Card title="Kiosk Mode" icon="tv">
    Deploy devices that automatically mirror another device's screen on boot, useful for digital signage or monitoring displays.

    ```bash theme={null}
    # Auto-start on boot using init script
    am start -n com.example.scrcpy/.MainActivity \
      --ez start_remote_headless true
    ```
  </Card>

  <Card title="Remote Support" icon="headset">
    Build remote support applications where users tap a "Get Help" button and their screen is automatically shared with support staff.

    ```java theme={null}
    // In your support app
    Intent intent = new Intent();
    intent.setComponent(new ComponentName(
        "com.example.scrcpy",
        "com.example.scrcpy.MainActivity"
    ));
    intent.putExtra("start_remote_headless", true);
    startActivity(intent);
    ```
  </Card>

  <Card title="Testing Automation" icon="flask">
    Automated testing frameworks can programmatically mirror device screens for visual verification or recording.

    ```python theme={null}
    # In your test script
    os.system("adb shell am start -n com.example.scrcpy/.MainActivity --ez start_remote_headless true")
    time.sleep(5)  # Wait for connection
    # Perform tests...
    ```
  </Card>

  <Card title="Monitoring Systems" icon="chart-line">
    Create dashboard applications that monitor multiple devices by automatically connecting in headless mode.

    ```java theme={null}
    // Launch multiple instances
    for (String deviceIp : deviceList) {
        // Set target in preferences first
        PreUtils.put(context, Constant.CONTROL_REMOTE_ADDR, deviceIp);
        
        Intent intent = new Intent(context, MainActivity.class);
        intent.putExtra(MainActivity.START_REMOTE, true);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
    ```
  </Card>
</CardGroup>

## Auto-Reconnect Behavior

Headless mode includes automatic reconnection logic when connections fail:

### Reconnect Dialog

When a connection fails or drops in headless mode, a dialog is automatically displayed (`MainActivity.java:816-831`):

```java theme={null}
if (headlessMode && !resumeScrcpy && !result_of_Rotation) {
    if (!userDisconnect) {
        Dialog.displayDialog(this, 
            getString(R.string.connect_faild),
            getString(R.string.connect_faild_ask), 
            () -> {
                // Retry connection
                connectScrcpyServer(PreUtils.get(context, 
                    Constant.CONTROL_REMOTE_ADDR, ""));
            }, 
            () -> {
                // Cancel - close app
                finishAndRemoveTask();
            }
        );
    }
}
```

### Reconnect Conditions

The auto-reconnect dialog appears only when:

<Tabs>
  <Tab title="Triggers">
    * Connection failure during initial connect
    * Unexpected disconnection during active session
    * Network timeout or error
  </Tab>

  <Tab title="Exceptions">
    Auto-reconnect does NOT trigger when:

    * `userDisconnect` is true (user manually disconnected)
    * `resumeScrcpy` is true (activity paused/resumed)
    * `result_of_Rotation` is true (screen rotation occurred)
  </Tab>
</Tabs>

### User Options

The dialog provides two options:

1. **Retry:** Attempts to reconnect using the same saved configuration
2. **Cancel:** Exits the application completely using `finishAndRemoveTask()`

<Note>
  In standard (non-headless) mode, connection failures simply return to the main UI. The auto-reconnect dialog is exclusive to headless mode.
</Note>

## Integration Scenarios

### Scenario 1: Boot-Time Auto-Connect

Create a boot receiver to start headless mode automatically:

```java theme={null}
public class BootReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
            // Delay to ensure network is ready
            Handler handler = new Handler(Looper.getMainLooper());
            handler.postDelayed(() -> {
                Intent launchIntent = new Intent(context, MainActivity.class);
                launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                launchIntent.putExtra(MainActivity.START_REMOTE, true);
                context.startActivity(launchIntent);
            }, 10000); // 10 second delay
        }
    }
}
```

**AndroidManifest.xml:**

```xml theme={null}
<receiver android:name=".BootReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
```

### Scenario 2: Tasker Integration

Use Tasker or similar automation apps:

**Task Configuration:**

1. Action: Send Intent
2. Action: `android.intent.action.MAIN`
3. Cat: `Default`
4. Package: `com.example.scrcpy`
5. Class: `com.example.scrcpy.MainActivity`
6. Extra: `start_remote_headless:true`
7. Target: `Activity`

### Scenario 3: Programmatic Configuration

Set the target device before launching headless mode:

```java theme={null}
public void connectToDevice(String deviceIp) {
    // Save target address to SharedPreferences
    SharedPreferences prefs = context.getSharedPreferences(
        "scrcpy_prefs", 
        Context.MODE_PRIVATE
    );
    prefs.edit()
        .putString(Constant.CONTROL_REMOTE_ADDR, deviceIp)
        .apply();
    
    // Launch in headless mode
    Intent intent = new Intent(context, MainActivity.class);
    intent.putExtra(MainActivity.START_REMOTE, true);
    startActivity(intent);
}
```

### Scenario 4: Tile Service Quick Connect

Create a Quick Settings tile for one-tap connection:

```java theme={null}
@RequiresApi(api = Build.VERSION_CODES.N)
public class ScrcpyTileService extends TileService {
    @Override
    public void onClick() {
        super.onClick();
        
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        intent.putExtra(MainActivity.START_REMOTE, true);
        startActivityAndCollapse(intent);
    }
}
```

## Configuration Requirements

For headless mode to work properly, the following must be pre-configured:

<AccordionGroup>
  <Accordion title="Target Device Address">
    Must be saved in `SharedPreferences` under key `Constant.CONTROL_REMOTE_ADDR`.

    **Setting:**

    ```java theme={null}
    PreUtils.put(context, Constant.CONTROL_REMOTE_ADDR, "192.168.1.100:5555");
    ```

    **Format:** `<ip_address>:<port>` (port defaults to 5555 if omitted)
  </Accordion>

  <Accordion title="Video Settings">
    Resolution, bitrate, and delay settings are loaded from saved preferences:

    * `PREFERENCE_SPINNER_RESOLUTION`: Video resolution index
    * `PREFERENCE_SPINNER_BITRATE`: Bitrate index
    * `PREFERENCE_SPINNER_DELAY`: Delay tolerance index

    These are set via the UI or programmatically using `PreUtils.put()`.
  </Accordion>

  <Accordion title="Control Settings">
    Touch and navigation settings:

    * `CONTROL_NO`: Disable touch control (boolean)
    * `CONTROL_NAV`: Show navigation bar (boolean)

    ```java theme={null}
    PreUtils.put(context, Constant.CONTROL_NO, false);
    PreUtils.put(context, Constant.CONTROL_NAV, true);
    ```
  </Accordion>
</AccordionGroup>

## Limitations

<Warning>
  Be aware of these limitations when using headless mode:
</Warning>

* **No UI Fallback:** If connection fails and user cancels retry, app exits entirely
* **No Configuration Changes:** Cannot change target device without exiting and modifying preferences
* **History Not Saved:** Connection history is disabled to avoid polluting logs with automated connections
* **Single Connection:** Only supports connecting to one saved device address

## Debugging Headless Mode

To troubleshoot headless mode issues:

```bash theme={null}
# Check if intent was received
adb logcat -s Scrcpy:I

# Look for:
# "headlessMode: true"
# Connection attempts
# Error messages

# Verify saved preferences
adb shell run-as com.example.scrcpy \
  cat /data/data/com.example.scrcpy/shared_prefs/scrcpy_prefs.xml
```

<Tip>
  For testing, manually set preferences via ADB before launching:

  ```bash theme={null}
  adb shell "am broadcast -a com.example.scrcpy.SET_PREF \
    --es key CONTROL_REMOTE_ADDR --es value '192.168.1.100:5555'"
  ```
</Tip>

## Exit Behavior

In headless mode, the app uses `finishAndRemoveTask()` instead of returning to the main UI:

```java theme={null}
finishAndRemoveTask(); // Completely removes app from recents
```

This ensures the app doesn't leave background tasks running when disconnected.
