Advanced AOSP Subsystems
4 min read

Multi-Display

Overview

The Android Multi-Display framework allows the OS to support multiple physical and virtual displays concurrently. Starting significantly in Android 10, the multi-display architecture was revamped to support foldables, automotive screens, and desktop-like external monitor experiences. This architectural shift required deep changes in DisplayManagerService, ActivityManagerService (now ActivityTaskManagerService), and WindowManagerService.

Multi-display Support in Android 10+

Prior to Android 10, Android treated secondary displays mostly as presentation surfaces. Activities on secondary displays were limited, and input focus was tightly coupled to the primary screen.

In Android 10+, the framework introduced the concept of Per-Display Focus and Multi-Resume. This allows each display to have its own focused activity, enabling true multitasking across multiple screens.

Key Architectural Changes:

  • Multi-Resume: All visible activities in focusable states can remain in the RESUMED state simultaneously.
  • Display Window Controller: The WindowManager creates a DisplayContent object for each logical display.
  • Input Dispatching: InputDispatcher routes input events not just based on the top-most window, but by determining which display the input device is associated with or which display the user interacted with.

Display Topology Management

The Android framework abstracts physical screens into logical displays. The hierarchy flows from the physical hardware (SurfaceFlinger and Hardware Composer) up to the logical management (DisplayManager).

  1. Hardware Composer (HWC): Exposes physical displays connected via DSI, HDMI, or DP.
  2. SurfaceFlinger: Manages compositing layers for each active display. It exposes physical displays to the framework via IBinder display tokens.
  3. DisplayManagerService (DMS): Receives logical display events and creates logical display representations. It maps physical display ports to logical display IDs (e.g., Display.DEFAULT_DISPLAY, which is typically ID 0).

Dumpsys Display

You can inspect the display topology using dumpsys:

adb shell dumpsys display

This output will show you the Logical Displays and Physical Displays. For example, it maps a physical display token to a logical display ID and shows the supported modes (resolution and refresh rate).

Activity Routing to Displays

When launching an Activity, developers or the system can specify which display it should appear on. This is handled by ActivityOptions.

Launching an Activity on a Specific Display

Here is a Java snippet demonstrating how to launch an Activity on a secondary display:

DisplayManager displayManager = (DisplayManager) getSystemService(Context.DISPLAY_SERVICE);
Display[] displays = displayManager.getDisplays();

if (displays.length > 1) {
    Display secondaryDisplay = displays[1];
    
    ActivityOptions options = ActivityOptions.makeBasic();
    options.setLaunchDisplayId(secondaryDisplay.getDisplayId());
    
    Intent intent = new Intent(this, SecondaryActivity.class);
    startActivity(intent, options.toBundle());
}

ActivityTaskManagerService (ATMS)

ATMS maintains a RootWindowContainer which contains multiple DisplayContent objects. When an activity is launched with a specific display ID, ATMS routes the task creation to the TaskDisplayArea associated with that DisplayContent.

DisplayManager Service

The DisplayManagerService (DMS) is the system service responsible for the lifecycle of displays. It listens to hardware events via SurfaceControl and exposes a high-level API to apps.

Core Components of DMS:

  • DisplayAdapters: DMS uses adapters to discover displays.
    • LocalDisplayAdapter: Finds built-in displays and external HDMI/DP screens via SurfaceFlinger.
    • WifiDisplayAdapter: Manages Miracast displays.
    • VirtualDisplayAdapter: Manages virtual displays created by apps.
  • LogicalDisplayMapper: Maps physical display devices to logical displays. This is crucial for handling foldables where folding the device might seamlessly transition the logical display from the inner screen to the outer screen.

Code Dive: SurfaceFlinger Display Token

In the native layer, SurfaceFlinger uses an IBinder token to uniquely identify a display. When a new physical display connects, SurfaceFlinger notifies DMS:

// frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::onHotplugReceived(int32_t sequenceId, hal::HWDisplayId hwcDisplayId,
                                       hal::Connection connection) {
    // Determine if connected or disconnected
    if (connection == hal::Connection::CONNECTED) {
        // Create a new display token
        sp<IBinder> token = new BBinder();
        mPhysicalDisplayTokens[hwcDisplayId] = token;
        // Notify DisplayManagerService
    }
}

This deep integration ensures that Android can dynamically react to hardware topology changes, making it robust for complex multi-screen environments.