版本比较

密钥

  • 该行被添加。
  • 该行被删除。
  • 格式已经改变。

...

ViewRootImpl中requestLayout函数调用mWindowSession.relayout,最终调用WindowManagerservice 的relayoutWindow,这个函数会配置Display信息; WindowStateAnimator创建Surfacecontrol,通过SurfaceControl,设置Layerstack。即在触发view更新绘制的时候才会更新图层数据。

代码块
languagejava
// com.android.server.wm.WindowManagerService#relayoutWindow

...

代码块
languagejava

if (shouldRelayout) {
    try {
        result = createSurfaceControl(outSurfaceControl, result, win, winAnimator);
    } catch (Exception e) {
        displayContent.getInputMonitor().updateInputWindowsLw(true /*force*/);
 
        ProtoLog.w(WM_ERROR,
                "Exception thrown when creating surface for client %s (%s). %s",
                client, win.mAttrs.getTitle(), e);
        Binder.restoreCallingIdentity(origId);
        return 0;
    }
}

至此,一个Windows对应一个ViewRootImpl,用来管理应用进程的view的绘制生命周期,事件机制等等,后续的流程是layer会在surfaceflinger里面处理后,经由GPU或者硬件合成渲染。

二、display创建流程

display主要由DisplayManagerService管理,DisplayManagerService是负责Display管理的系统服务之一,还有一些其他功能,包括屏幕亮度调节也涉及到DMS,其继承自SystemService,因此具有SystemService子类的共性:具有生命周期方法,由SystemServer启动、注册到系统服务中,通过Binder和其他组件进行交互等。

...

Display类的类型有四种:LocalDisplay、OverlayDisplay、WifiDisplay和VirtualDisplay。每一个Display对应一个DisplayAdapter。

  • LocalDisplayAdapter:本地已经存在的物理显示屏设备。

  • OverlayDisplayAdapter:模拟辅助显示设备,以类似浮动窗口的形式显示在主屏上,可以当第二个屏幕使用,默认也是镜像主屏。

  • WifiDisplayAdapter:WiFi Display

  • VirtualDisplayAdapter:显示一个虚拟屏幕

不同的类型的display的创建过程大同小异,在DMS里面的数据结构是DisplayDevice,不同类型的DisplayDevice都是由不同类型的DisplayAdapter创建的。虚拟屏mVirtualDisplayAdapter并没有在这里创建,在后面调用到的地方再创建。

代码块
languagejava
//注册主屏适配器
    private void registerDefaultDisplayAdapters() {
        // Register default display adapters.
        synchronized (mSyncRoot) {
            // main display adapter
            registerDisplayAdapterLocked(new LocalDisplayAdapter(
                    mSyncRoot, mContext, mHandler, mDisplayDeviceRepo));
 
            // Standalone VR devices rely on a virtual display as their primary display for
            // 2D UI. We register virtual display adapter along side the main display adapter
            // here so that it is ready by the time the system sends the home Intent for
            // early apps like SetupWizard/Launcher. In particular, SUW is displayed using
            // the virtual display inside VR before any VR-specific apps even run.
            mVirtualDisplayAdapter = mInjector.getVirtualDisplayAdapter(mSyncRoot, mContext,
                    mHandler, mDisplayDeviceRepo);
            if (mVirtualDisplayAdapter != null) {
                registerDisplayAdapterLocked(mVirtualDisplayAdapter);
            }
        }
    }
    private void registerAdditionalDisplayAdapters() {
        synchronized (mSyncRoot) {
            if (shouldRegisterNonEssentialDisplayAdaptersLocked()) {
                registerOverlayDisplayAdapterLocked();
                registerWifiDisplayAdapterLocked();
            }
        }
    }
 
    private void registerOverlayDisplayAdapterLocked() {
        registerDisplayAdapterLocked(new OverlayDisplayAdapter(
                mSyncRoot, mContext, mHandler, mDisplayDeviceRepo, mUiHandler));
    }
 
    private void registerWifiDisplayAdapterLocked() {
        if (mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_enableWifiDisplay)
                || SystemProperties.getInt(FORCE_WIFI_DISPLAY_ENABLE, -1) == 1) {
            mWifiDisplayAdapter = new WifiDisplayAdapter(
                    mSyncRoot, mContext, mHandler, mDisplayDeviceRepo,
                    mPersistentDataStore);
            registerDisplayAdapterLocked(mWifiDisplayAdapter);
        }
    }

以VirtualDisplayDevice的创建为例来说明一个display创建过程,不同的安卓版本这部分代码有不少变动,以sdK-31为例:

应用进程调用到DisplayManagerGlobal,全局只有这一个,在里面提供了获取创建VirtualDisplay的方法。

代码块
languagejava
public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
        String name, int width, int height, int densityDpi, Surface surface, int flags,
        VirtualDisplay.Callback callback, Handler handler, String uniqueId) 

获取所有逻辑id的方法getDisplayIds,和根据id返回info的方法,都是IPC调用到DisplayManagerService返回数据,可以遍历disaplyinfo。

代码块
languagejava
public int[] getDisplayIds() {
    try {
        synchronized (mLock) {
            if (USE_CACHE) {
                if (mDisplayIdCache != null) {
                    return mDisplayIdCache;
                }
            }
            int[] displayIds = mDm.getDisplayIds();
...
            return displayIds;
        }
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}
代码块
languagejava
public DisplayInfo getDisplayInfo(int displayId) {
    try {
        synchronized (mLock) {
            DisplayInfo info;
            if (USE_CACHE) {
                info = mDisplayInfoCache.get(displayId);
                if (info != null) {
                    return info;
                }
            }
 
            info = mDm.getDisplayInfo(displayId);
...
            return info;
        }
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
}

VirtualDisplayDevice创建流程如下:

...

经过如下三个函数的调用就创建了VirtualDisplayDevice,但是这时候只是创建好VirtualDisplay。

代码块
languagejava
com.android.server.display.DisplayManagerService#createVirtualDisplayInternal
com.android.server.display.VirtualDisplayAdapter#createVirtualDisplayLocked
com.android.server.display.VirtualDisplayAdapter.VirtualDisplayDevice#VirtualDisplayDevice

三、VirtualDisplayDevice的显示内容

创建后需要对DisplayDevices遍历处理,更新,同时也会加入到LogicDisplay。

代码块
languagejava
private void handleDisplayDeviceAddedLocked(DisplayDevice device) {
    DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
    if (mDisplayDevices.contains(device)) {
        Slog.w(TAG, "Attempted to add already added display device: " + info);
        return;
    }
 
    Slog.i(TAG, "Display device added: " + info);
    device.mDebugLastLoggedDeviceInfo = info;
 
    mDisplayDevices.add(device);
    LogicalDisplay display = addLogicalDisplayLocked(device);
    Runnable work = updateDisplayStateLocked(device);
    if (work != null) {
        work.run();
    }
    scheduleTraversalLocked(false);

遍历过程会对Device进行映射。默认是映射到Display.DEFAULT_DISPLAY即物理主屏。

代码块
languagejava
private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {
        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
        final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
 
        // Find the logical display that the display device is showing.
        // Certain displays only ever show their own content.
        LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
        // Proceed with display-managed mirroring only if window manager will not be handling it.
        if (!ownContent && !device.isWindowManagerMirroringLocked()) {
            // Only mirror the display if content recording is not taking place in WM.
            if (display != null && !display.hasContentLocked()) {
                // If the display does not have any content of its own, then
                // automatically mirror the requested logical display contents if possible.
                display = mLogicalDisplayMapper.getDisplayLocked(
                        device.getDisplayIdToMirrorLocked());
            }
            if (display == null) {
                display = mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY);
            }
        }
 
        // Apply the logical display configuration to the display device.
        if (display == null) {
            // TODO: no logical display for the device, blank it
            Slog.w(TAG, "Missing logical display to use for physical display device: "
                    + device.getDisplayDeviceInfoLocked());
            return;
        }
        display.configureDisplayLocked(t, device, info.state == Display.STATE_OFF);
        final Optional<Integer> viewportType = getViewportType(info);
        if (viewportType.isPresent()) {
            populateViewportLocked(viewportType.get(), display.getDisplayIdLocked(), device, info);
        }
    }

映射完成后会调用LogicDisplayDevice.configureDisplayLocked对显示的内容layer进行传递。

代码块
languagejava
public void configureDisplayLocked(SurfaceControl.Transaction t,
        DisplayDevice device,
        boolean isBlanked) {
    // Set the layer stack.
    device.setLayerStackLocked(t, isBlanked ? BLANK_LAYER_STACK : mLayerStack);
 
    // Set the color mode and allowed display mode.
    if (device == mPrimaryDisplayDevice) {
        device.setAllowedDisplayModesLocked(mAllowedDisplayModes);
        device.setRequestedColorModeLocked(mRequestedColorMode);
    } else {
        // Reset to default for non primary displays
        device.setAllowedDisplayModesLocked(new int[] {0});
        device.setRequestedColorModeLocked(0);
    }
  ....
}

此函数注释如下,意思是这个device可以不显示成主屏的内容,要看被你镜像的device是谁:

代码块
languagejava
/**
 * Applies the layer stack and transformation to the given display device
 * so that it shows the contents of this logical display.
 *
 * We know that the given display device is only ever showing the contents of
 * a single logical display, so this method is expected to blow away all of its
 * transformation properties to make it happen regardless of what the
 * display device was previously showing.
 *
 * The caller must have an open Surface transaction.
 *
 * The display device may not be the primary display device, in the case
 * where the display is being mirrored.
 *
 * @param device The display device to modify.
 * @param isBlanked True if the device is being blanked.
 */

也就是这段代码device.getDisplayIdToMirrorLocked():

代码块
languagejava
 private void configureDisplayLocked(SurfaceControl.Transaction t, DisplayDevice device) {
        final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
        final boolean ownContent = (info.flags & DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY) != 0;
 
        // Find the logical display that the display device is showing.
        // Certain displays only ever show their own content.
        LogicalDisplay display = mLogicalDisplayMapper.getDisplayLocked(device);
        // Proceed with display-managed mirroring only if window manager will not be handling it.
        if (!ownContent && !device.isWindowManagerMirroringLocked()) {
            // Only mirror the display if content recording is not taking place in WM.
            if (display != null && !display.hasContentLocked()) {
                // If the display does not have any content of its own, then
                // automatically mirror the requested logical display contents if possible.
                display = mLogicalDisplayMapper.getDisplayLocked(
                        device.getDisplayIdToMirrorLocked());
            }
            if (display == null) {
                display = mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY);
            }
        }
    ...

在com.android.server.display.VirtualDisplayAdapter.VirtualDisplayDevice 里面这getDisplayIdToMirrorLocked被重写,这里可以指定自己想要镜像的ID,0,2,3...(已验证),可以实现的功能是,录像,截屏,分享等可以拿取到指定的屏幕。

代码块
public int getDisplayIdToMirrorLocked() {
    return mDisplayIdToMirror;
}

这个mDisplayIdToMirror是在VirtualDisplayConfig里面的成员变量。

代码块
public VirtualDisplay createVirtualDisplay(@NonNull String name,
        int width, int height, int dpi, boolean isSecure, @Nullable Surface surface,
        @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
    int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR
            | DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
    if (isSecure) {
        flags |= DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
    }
    final VirtualDisplayConfig.Builder builder = new VirtualDisplayConfig.Builder(name, width,
            height, dpi).setFlags(flags);
    if (surface != null) {
        builder.setSurface(surface);
    }
    VirtualDisplay virtualDisplay = createVirtualDisplay(builder, callback, handler);
    return virtualDisplay;
}

VirtualDisplayConfig的建造函数提供了setDisplayIdToMirror,此处是否可以实现指定被镜像的display待验证。

代码块
@DataClass.Generated.Member
public @NonNull Builder setDisplayIdToMirror(int value) {
    checkNotUsed();
    mBuilderFieldsSet |= 0x80;
    mDisplayIdToMirror = value;
    return this;
}

下一步可以分析,创建后正在绘制的display,动态更新到不同的指定屏上...

参考资料