a. display & window之显示

window对应用层来说来,是view跟display解耦,对framework来说,是用来display跟layer(即surfaceflinger)解耦,简单的理解:display ->layer/view<-window。

从显示的角度来看,并没有windows这个概念,只有layer图层,display显示屏的概念,window提供了一个桥梁来连接display和layer,因为安卓系统只允许应用处理自己的view关系,而且display并不属于应用可以管理的范围,于是就在activity的生命周期加了入window来桥接displayt和view,window决定了view的显示,具体window和display创建的流程如下:

一、window创建流程

image-20240206-083050.png

如上图,应用进程activity的生命周期函数里面按步骤创建window,view,关联view和window,会binder调到WMS的addwindow。

注:此处的WindowSession.addToDisplay 应为**com.android.server.wm**.Session

WMS, WindowToken 里的displaycontent即是Display。

addToDisplay 里会执行  WindowManagerService的addwindow。

@Override public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { return mService.addWindow(this, window, attrs, viewVisibility, displayId, UserHandle.getUserId(mUid), requestedVisibilities, outInputChannel, outInsetsState, outActiveControls); } @Override public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) { return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId, requestedVisibilities, outInputChannel, outInsetsState, outActiveControls); } @Override public int addToDisplayWithoutInputChannel(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, InsetsState outInsetsState) { return mService.addWindow(this, window, attrs, viewVisibility, displayId, UserHandle.getUserId(mUid), mDummyRequestedVisibilities, null /* outInputChannel */, outInsetsState, mDummyControls); }

在addwindow中对应不同场景。

final boolean openInputChannels = (outInputChannel != null && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0); if (openInputChannels) { win.openInputChannel(outInputChannel); } int userId = UserHandle.getUserId(session.mUid); if (requestUserId != userId) { try { mAmInternal.handleIncomingUser(callingPid, callingUid, requestUserId, false /*allowAll*/, ALLOW_NON_FULL, null, null); } catch (Exception exp) { ProtoLog.w(WM_ERROR, "Trying to add window with invalid user=%d", requestUserId); return WindowManagerGlobal.ADD_INVALID_USER; } // It's fine to use this userId userId = requestUserId; }

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

// com.android.server.wm.WindowManagerService#relayoutWindow 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和其他组件进行交互等。

image-20240207-011625.png

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

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

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

  • WifiDisplayAdapter:WiFi Display

  • VirtualDisplayAdapter:显示一个虚拟屏幕

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

//注册主屏适配器 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的方法。

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。

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(); } }
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创建流程如下:

image-20240207-023802.png

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

com.android.server.display.DisplayManagerService#createVirtualDisplayInternal com.android.server.display.VirtualDisplayAdapter#createVirtualDisplayLocked com.android.server.display.VirtualDisplayAdapter.VirtualDisplayDevice#VirtualDisplayDevice

三、VirtualDisplayDevice的显示内容

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

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即物理主屏。

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进行传递。

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是谁:

/** * 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():

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,动态更新到不同的指定屏上...

参考资料