Android WMS 之 DisplayContent

2023-09-10  本文已影响0人  行走中的3卡

窗口管理核心类: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

上一篇下一篇

猜你喜欢

热点阅读