美文网首页
Android WMS 之 DisplayContent

Android WMS 之 DisplayContent

作者: 行走中的3卡 | 来源:发表于2023-09-10 15:29 被阅读0次

    窗口管理核心类:DisplayContent,WindowToken和WindowState

    1. 概念

    Android WMS 中 DisplayContent 用来管理一个逻辑屏上的所有窗口,
    有几个屏幕就会有几个DisplayContent。使用displayId来区分。

    2. 源码

    基于 Android 14 源码. (20230911 codesearch main 分支)
    frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    package com.android.server.wm;
    /**
     * Utility class for keeping track of the WindowStates and other pertinent contents of a
     * particular Display.
     */
    class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {
        private static final String TAG = TAG_WITH_CLASS_NAME ? "DisplayContent" : TAG_WM;
    }
    

    用于跟踪特定 Display 的 WindowStates 和其他相关内容的 辅助类。
    可以看到它继承了 RootDisplayArea, 以及实现了接口 WindowManagerPolicy.DisplayContentInfo

    2.1 构造方法

    里面注入两个参数,分别为 Display 和 RootWindowContainer 的对象.
    显示的内容由 Display 提供, 包含displayId/displayInfo/displayMetrics 等

        /**
         * Create new {@link DisplayContent} instance, add itself to the root window container and
         * initialize direct children.
         * @param display May not be null.
         * @param root {@link RootWindowContainer}
         */
        DisplayContent(Display display, RootWindowContainer root) {
            super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
            if (mWmService.mRoot.getDisplayContent(display.getDisplayId()) != null) {
                throw new IllegalArgumentException("Display with ID=" + display.getDisplayId()
                        + " already exists="
                        + mWmService.mRoot.getDisplayContent(display.getDisplayId())
                        + " new=" + display);
            }
    
            mRootWindowContainer = root;
            mAtmService = mWmService.mAtmService;
            mDisplay = display;
            mDisplayId = display.getDisplayId();
            mCurrentUniqueDisplayId = display.getUniqueId();
            mOffTokenAcquirer = mRootWindowContainer.mDisplayOffTokenAcquirer;
            mWallpaperController = new WallpaperController(mWmService, this);
            mWallpaperController.resetLargestDisplay(display);
            display.getDisplayInfo(mDisplayInfo);
            display.getMetrics(mDisplayMetrics);
            mSystemGestureExclusionLimit = mWmService.mConstants.mSystemGestureExclusionLimitDp
                    * mDisplayMetrics.densityDpi / DENSITY_DEFAULT;
            isDefaultDisplay = mDisplayId == DEFAULT_DISPLAY;
            mInsetsStateController = new InsetsStateController(this);
            mDisplayFrames = new DisplayFrames(mInsetsStateController.getRawInsetsState(),
                    mDisplayInfo, calculateDisplayCutoutForRotation(mDisplayInfo.rotation),
                    calculateRoundedCornersForRotation(mDisplayInfo.rotation),
                    calculatePrivacyIndicatorBoundsForRotation(mDisplayInfo.rotation));
            initializeDisplayBaseInfo();
    
            mHoldScreenWakeLock = mWmService.mPowerManager.newWakeLock(
                    PowerManager.SCREEN_BRIGHT_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE,
                    TAG_WM + "/displayId:" + mDisplayId, mDisplayId);
            mHoldScreenWakeLock.setReferenceCounted(false);
    
            mAppTransition = new AppTransition(mWmService.mContext, mWmService, this);
            mAppTransition.registerListenerLocked(mWmService.mActivityManagerAppTransitionNotifier);
            mAppTransition.registerListenerLocked(mFixedRotationTransitionListener);
            mAppTransitionController = new AppTransitionController(mWmService, this);
            mTransitionController.registerLegacyListener(mFixedRotationTransitionListener);
            mUnknownAppVisibilityController = new UnknownAppVisibilityController(mWmService, this);
            mDisplaySwitchTransitionLauncher = new PhysicalDisplaySwitchTransitionLauncher(this,
                    mTransitionController);
            mRemoteDisplayChangeController = new RemoteDisplayChangeController(mWmService, mDisplayId);
    
            final InputChannel inputChannel = mWmService.mInputManager.monitorInput(
                    "PointerEventDispatcher" + mDisplayId, mDisplayId);
            mPointerEventDispatcher = new PointerEventDispatcher(inputChannel);
    
            // Tap Listeners are supported for:
            // 1. All physical displays (multi-display).
            // 2. VirtualDisplays on VR, AA (and everything else).
            mTapDetector = new TaskTapPointerEventListener(mWmService, this);
            registerPointerEventListener(mTapDetector);
            registerPointerEventListener(mWmService.mMousePositionTracker);
            if (mWmService.mAtmService.getRecentTasks() != null) {
                registerPointerEventListener(
                        mWmService.mAtmService.getRecentTasks().getInputListener());
            }
    
            mDeviceStateController = new DeviceStateController(mWmService.mContext, mWmService.mH);
    
            mDisplayPolicy = new DisplayPolicy(mWmService, this);
            mDisplayRotation = new DisplayRotation(mWmService, this, mDisplayInfo.address,
                    mDeviceStateController);
    
            final Consumer<DeviceStateController.DeviceState> deviceStateConsumer =
                    (@NonNull DeviceStateController.DeviceState newFoldState) -> {
                        mDisplaySwitchTransitionLauncher.foldStateChanged(newFoldState);
                        mDisplayRotation.foldStateChanged(newFoldState);
                    };
            mDeviceStateController.registerDeviceStateCallback(deviceStateConsumer);
    
            mCloseToSquareMaxAspectRatio = mWmService.mContext.getResources().getFloat(
                    R.dimen.config_closeToSquareDisplayMaxAspectRatio);
            if (isDefaultDisplay) {
                // The policy may be invoked right after here, so it requires the necessary default
                // fields of this display content.
                mWmService.mPolicy.setDefaultDisplay(this);
            }
            if (mWmService.mDisplayReady) {
                mDisplayPolicy.onConfigurationChanged();
            }
            if (mWmService.mSystemReady) {
                mDisplayPolicy.systemReady();
            }
            mWindowCornerRadius = mDisplayPolicy.getWindowCornerRadius();
            mDividerControllerLocked = new DockedTaskDividerController(this);
            mPinnedTaskController = new PinnedTaskController(mWmService, this);
    
            final Transaction pendingTransaction = getPendingTransaction();
            configureSurfaces(pendingTransaction);
            pendingTransaction.apply();
    
            // Sets the display content for the children.
            onDisplayChanged(this);
            updateDisplayAreaOrganizers();
    
            mDisplayRotationCompatPolicy =
                    // Not checking DeviceConfig value here to allow enabling via DeviceConfig
                    // without the need to restart the device.
                    mWmService.mLetterboxConfiguration.isCameraCompatTreatmentEnabled(
                                /* checkDeviceConfig */ false)
                            ? new DisplayRotationCompatPolicy(this) : null;
            mRotationReversionController = new DisplayRotationReversionController(this);
    
            mInputMonitor = new InputMonitor(mWmService, this);
            mInsetsPolicy = new InsetsPolicy(mInsetsStateController, this);
            mMinSizeOfResizeableTaskDp = getMinimalTaskSizeDp();
            if (DEBUG_DISPLAY) Slog.v(TAG_WM, "Creating display=" + display);
    
            setWindowingMode(WINDOWING_MODE_FULLSCREEN);
            mWmService.mDisplayWindowSettings.applySettingsToDisplayLocked(this);
        }
    
    

    2.2 关键方法

    migrateToNewSurfaceControl()

        @Override
        void migrateToNewSurfaceControl(Transaction t) {
            t.remove(mSurfaceControl);
    
            mLastSurfacePosition.set(0, 0);
            mLastDeltaRotation = Surface.ROTATION_0;
    
            configureSurfaces(t);
    
            for (int i = 0; i < mChildren.size(); i++)  {
                SurfaceControl sc = mChildren.get(i).getSurfaceControl();
                if (sc != null) {
                    t.reparent(sc, mSurfaceControl);
                }
            }
    
            scheduleAnimation();
        }
    
        /**
    

    addWindowToken

        void addWindowToken(IBinder binder, WindowToken token) {
            final DisplayContent dc = mWmService.mRoot.getWindowTokenDisplay(token);
            if (dc != null) {
                // We currently don't support adding a window token to the display if the display
                // already has the binder mapped to another token. If there is a use case for supporting
                // this moving forward we will either need to merge the WindowTokens some how or have
                // the binder map to a list of window tokens.
                throw new IllegalArgumentException("Can't map token=" + token + " to display="
                        + getName() + " already mapped to display=" + dc + " tokens=" + dc.mTokenMap);
            }
            if (binder == null) {
                throw new IllegalArgumentException("Can't map token=" + token + " to display="
                        + getName() + " binder is null");
            }
            if (token == null) {
                throw new IllegalArgumentException("Can't map null token to display="
                        + getName() + " binder=" + binder);
            }
    
            mTokenMap.put(binder, token);
    
            if (token.asActivityRecord() == null) {
                // Set displayContent for non-app token to prevent same token will add twice after
                // onDisplayChanged.
                // TODO: Check if it's fine that super.onDisplayChanged of WindowToken
                //  (WindowsContainer#onDisplayChanged) may skipped when token.mDisplayContent assigned.
                token.mDisplayContent = this;
                // Add non-app token to container hierarchy on the display. App tokens are added through
                // the parent container managing them (e.g. Tasks).
                final DisplayArea.Tokens da = findAreaForToken(token).asTokens();
                da.addChild(token);
            }
        }
    

    updateImeParent

       void updateImeParent() {
            if (mImeWindowsContainer.isOrganized()) {
                if (DEBUG_INPUT_METHOD) {
                    Slog.i(TAG_WM, "ImeContainer is organized. Skip updateImeParent.");
                }
                // Leave the ImeContainer where the DisplayAreaPolicy placed it.
                // FEATURE_IME is organized by vendor so they are responible for placing the surface.
                mInputMethodSurfaceParent = null;
                return;
            }
    
            final SurfaceControl newParent = computeImeParent();
            if (newParent != null && newParent != mInputMethodSurfaceParent) {
                mInputMethodSurfaceParent = newParent;
                getSyncTransaction().reparent(mImeWindowsContainer.mSurfaceControl, newParent);
                // When surface parent is removed, the relative layer will also be removed. We need to
                // do a force update to make sure there is a layer set for the new parent.
                assignRelativeLayerForIme(getSyncTransaction(), true /* forceUpdate */);
                scheduleAnimation();
    
                mWmService.mH.post(() -> InputMethodManagerInternal.get().onImeParentChanged());
            }
        }
    

    999. 参考

    https://zhuanlan.zhihu.com/p/588860452 (不错,介绍了DisplayConten/WindowToken/WindowState/Surface... )

    其它相关
    Android Surface 学习笔记: https://www.jianshu.com/p/b496c7850d4a

    在UI上看到的每一个window(如对话框、全屏的activity、状态栏)都有唯一一个自己的surface,
    window将自己的内容(content)绘制到该 surface中。
    所以从上层到底层: Activity - Window - Surface

    相关文章

      网友评论

          本文标题:Android WMS 之 DisplayContent

          本文链接:https://www.haomeiwen.com/subject/oqpsvdtx.html