Android 9.0系统源码_SystemUIPhoneWindowManager构建状态栏和导航栏视图窗口区域坐标的流程解析

Posted AFinalStone

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 9.0系统源码_SystemUIPhoneWindowManager构建状态栏和导航栏视图窗口区域坐标的流程解析相关的知识,希望对你有一定的参考价值。

前言

NavigationBar 和 StatusBar 都属于 SystemBar,也叫做 decor,就是说给 App 装饰的意思。一般的 window 的布局是在 PhoneWindowManager 的 layoutWindowLw() 方法中,而 SystemBar 是在 beginLayoutLw() 方法中布局。

当前最上层的 Activity 可以修改 SystemBar 的 visibility,可以调用 View#setSystemUiVisibility() 方法,系统也有一些针对 SystemBar visibility 的策略。最终的 visibility 保存在 PhoneWindowManager 中的 mLastSystemUiFlags 变量中。

一、简单认识DisplayFrames

在分析NaivgationBar和StatusBar对应的窗口布局前,需要我们先来简单认识android中的DisplayFrames对象。

frameworks/base/services/core/java/com/android/server/wm/DisplayFrames.java

public class DisplayFrames 
    //物理屏幕相关的设备id
    public final int mDisplayId;

    /**
     * The current size of the screen; really; extends into the overscan area of the screen and
     * doesn't account for any system elements like the status bar.
     */
    //当前的屏幕大小,包括过扫描区域。过扫描区域在输出到 TV 时会用到,
    // 对于移动设备来说 mOverscan 大小就是物理设备的大小 (0,0)-(dw,dh)。
    public final Rect mOverscan = new Rect();

    /**
     * The current visible size of the screen; really; (ir)regardless of whether the status bar can
     * be hidden but not extending into the overscan area.
     */
    //当前可见的屏幕大小,其实就是 (0,0)-(dw,dh)。
    public final Rect mUnrestricted = new Rect();

    /** Like mOverscan*, but allowed to move into the overscan region where appropriate. */
    //是应用可显示的区域,包含 StatusBar 的区域,不包含 NavigationBar 区域。
    public final Rect mRestrictedOverscan = new Rect();
    /**
     * The current size of the screen; these may be different than (0,0)-(dw,dh) if the status bar
     * can't be hidden; in that case it effectively carves out that area of the display from all
     * other windows.
     */
    //一般情况下 mRestrictedOverscan 与 mRestricted 相同
    public final Rect mRestricted = new Rect();

    /**
     * During layout, the current screen borders accounting for any currently visible system UI
     * elements.
     */
    //是布局过程中,当前画面的边界,包含 Translucent(半透明)区域。一般情况下 NavigationBar 区域不是 Translucent,而 StatusBar 是 Translucent。
    public final Rect mSystem = new Rect();

    /** For applications requesting stable content insets, these are them. */
    //是应用窗口的显示区域,不包含 StatusBar 和 NavigationBar。
    public final Rect mStable = new Rect();

    /**
     * For applications requesting stable content insets but have also set the fullscreen window
     * flag, these are the stable dimensions without the status bar.
     */
    //是当Activity设置Fullscreen flag 时候的窗口显示区域,这时 StatusBar 会隐藏。
    public final Rect mStableFullscreen = new Rect();

    /**
     * During layout, the current screen borders with all outer decoration (status bar, input method
     * dock) accounted for.
     */
    // 是布局的时候除去外部装饰的窗口(例如 StatusBar 和输入法窗口)。
    public final Rect mCurrent = new Rect();

    /**
     * During layout, the frame in which content should be displayed to the user, accounting for all
     * screen decoration except for any space they deem as available for other content. This is
     * usually the same as mCurrent*, but may be larger if the screen decor has supplied content
     * insets.
     */
    // 是当前应该给用户显示的窗口,通常与 mCurrent 相同。当装饰窗口提供内容插入的时候,有可能比 mCurrent 更大。
    public final Rect mContent = new Rect();

    /**
     * During layout, the frame in which voice content should be displayed to the user, accounting
     * for all screen decoration except for any space they deem as available for other content.
     */
    //mVoiceContent 通常与 mContent 相同。
    public final Rect mVoiceContent = new Rect();

    /** During layout, the current screen borders along which input method windows are placed. */
    //mDock 是输入法布局时的边界。
    public final Rect mDock = new Rect();

    /** The display cutout used for layout (after rotation) */
    //用于刘海屏布局的剪刀工具,Android 9.0 新加入的。
    @NonNull public WmDisplayCutout mDisplayCutout = WmDisplayCutout.NO_CUTOUT;

    /** The cutout as supplied by display info */
    //用于刘海屏布局的剪刀工具,Android 9.0 新加入的。
    @NonNull public WmDisplayCutout mDisplayInfoCutout = WmDisplayCutout.NO_CUTOUT;

    /**
     * During layout, the frame that is display-cutout safe, i.e. that does not intersect with it.
     */
    //是在刘海屏上可以安全显示的区域,即这个区域与刘海区域没有交集。
    public final Rect mDisplayCutoutSafe = new Rect();

    private final Rect mDisplayInfoOverscan = new Rect();
    private final Rect mRotatedDisplayInfoOverscan = new Rect();

    public int mDisplayWidth;//物理屏幕宽度
    public int mDisplayHeight;//物理屏幕高度

    public int mRotation;//屏幕旋转角度

    public DisplayFrames(int displayId, DisplayInfo info, WmDisplayCutout displayCutout) 
        mDisplayId = displayId;
        onDisplayInfoUpdated(info, displayCutout);
    

    public void onDisplayInfoUpdated(DisplayInfo info, WmDisplayCutout displayCutout) 
        mDisplayWidth = info.logicalWidth;
        mDisplayHeight = info.logicalHeight;
        mRotation = info.rotation;
        mDisplayInfoOverscan.set(
                info.overscanLeft, info.overscanTop, info.overscanRight, info.overscanBottom);
        mDisplayInfoCutout = displayCutout != null ? displayCutout : WmDisplayCutout.NO_CUTOUT;
    

    public void onBeginLayout() 
        switch (mRotation) 
            case ROTATION_90:
                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.top;
                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.right;
                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.bottom;
                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.left;
                break;
            case ROTATION_180:
                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.right;
                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.bottom;
                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.left;
                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.top;
                break;
            case ROTATION_270:
                mRotatedDisplayInfoOverscan.left = mDisplayInfoOverscan.bottom;
                mRotatedDisplayInfoOverscan.top = mDisplayInfoOverscan.left;
                mRotatedDisplayInfoOverscan.right = mDisplayInfoOverscan.top;
                mRotatedDisplayInfoOverscan.bottom = mDisplayInfoOverscan.right;
                break;
            default:
                mRotatedDisplayInfoOverscan.set(mDisplayInfoOverscan);
                break;
        

        mRestrictedOverscan.set(0, 0, mDisplayWidth, mDisplayHeight);
        mOverscan.set(mRestrictedOverscan);
        mSystem.set(mRestrictedOverscan);
        mUnrestricted.set(mRotatedDisplayInfoOverscan);
        mUnrestricted.right = mDisplayWidth - mUnrestricted.right;
        mUnrestricted.bottom = mDisplayHeight - mUnrestricted.bottom;
        mRestricted.set(mUnrestricted);
        mDock.set(mUnrestricted);
        mContent.set(mUnrestricted);
        mVoiceContent.set(mUnrestricted);
        mStable.set(mUnrestricted);
        mStableFullscreen.set(mUnrestricted);
        mCurrent.set(mUnrestricted);

        mDisplayCutout = mDisplayInfoCutout;
        mDisplayCutoutSafe.set(Integer.MIN_VALUE, Integer.MIN_VALUE,
                Integer.MAX_VALUE, Integer.MAX_VALUE);
        if (!mDisplayCutout.getDisplayCutout().isEmpty()) 
            final DisplayCutout c = mDisplayCutout.getDisplayCutout();
            if (c.getSafeInsetLeft() > 0) 
                mDisplayCutoutSafe.left = mRestrictedOverscan.left + c.getSafeInsetLeft();
            
            if (c.getSafeInsetTop() > 0) 
                mDisplayCutoutSafe.top = mRestrictedOverscan.top + c.getSafeInsetTop();
            
            if (c.getSafeInsetRight() > 0) 
                mDisplayCutoutSafe.right = mRestrictedOverscan.right - c.getSafeInsetRight();
            
            if (c.getSafeInsetBottom() > 0) 
                mDisplayCutoutSafe.bottom = mRestrictedOverscan.bottom - c.getSafeInsetBottom();
            
        
    


二、构建SystemBar对应的视图窗口区域坐标对象

1、系统主要是在PhoneWindowManager的beginLayoutLw() 方法中构建SystemBar对应的视图窗口区域坐标对象的。

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

public class PhoneWindowManager implements WindowManagerPolicy 

    @Override
    public void beginLayoutLw(DisplayFrames displayFrames, int uiMode) 
        displayFrames.onBeginLayout();
        // TODO(multi-display): This doesn't seem right...Maybe only apply to default display?
        mSystemGestures.screenWidth = displayFrames.mUnrestricted.width();
        mSystemGestures.screenHeight = displayFrames.mUnrestricted.height();
        mDockLayer = 0x10000000;
        mStatusBarLayer = -1;

        // start with the current dock rect, which will be (0,0,displayWidth,displayHeight)
        final Rect pf = mTmpParentFrame;
        final Rect df = mTmpDisplayFrame;
        final Rect of = mTmpOverscanFrame;
        final Rect vf = mTmpVisibleFrame;
        final Rect dcf = mTmpDecorFrame;
        vf.set(displayFrames.mDock);
        of.set(displayFrames.mDock);
        df.set(displayFrames.mDock);
        pf.set(displayFrames.mDock);
        dcf.setEmpty();  // Decor frame N/A for system bars.

        if (displayFrames.mDisplayId == DEFAULT_DISPLAY) 
            // For purposes of putting out fake window up to steal focus, we will
            // drive nav being hidden only by whether it is requested.
            //获取窗口systemui的标记类型
            final int sysui = mLastSystemUiFlags;
            //navigationBar是否可见
            boolean navVisible = (sysui & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
            //navigationBar是否是半透明的
            boolean navTranslucent = (sysui & (View.NAVIGATION_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSPARENT)) != 0;
            boolean immersive = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
            boolean immersiveSticky = (sysui & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
            //navigationBar是否允许隐藏
            boolean navAllowedHidden = immersive || immersiveSticky;
            navTranslucent &= !immersiveSticky;  // transient trumps translucent
            boolean isKeyguardShowing = isStatusBarKeyguard() && !mKeyguardOccluded;
            if (!isKeyguardShowing) 
                navTranslucent &= areTranslucentBarsAllowed();
            
            boolean statusBarExpandedNotKeyguard = !isKeyguardShowing && mStatusBar != null
                    && mStatusBar.getAttrs().height == MATCH_PARENT
                    && mStatusBar.getAttrs().width == MATCH_PARENT;

            // When the navigation bar isn't visible, we put up a fake input window to catch all
            // touch events. This way we can detect when the user presses anywhere to bring back the
            // nav bar and ensure the application doesn't see the event.
            if (navVisible || navAllowedHidden) 
                if (mInputConsumer != null) 
                    mHandler.sendMessage(
                            mHandler.obtainMessage(MSG_DISPOSE_INPUT_CONSUMER, mInputConsumer));
                    mInputConsumer = null;
                
             else if (mInputConsumer == null && mStatusBar != null && canHideNavigationBar()) 
                mInputConsumer = mWindowManagerFuncs.createInputConsumer(mHandler.getLooper(),
                        INPUT_CONSUMER_NAVIGATION,
                        (channel, looper) -> new HideNavInputEventReceiver(channel, looper));
                // As long as mInputConsumer is active, hover events are not dispatched to the app
                // and the pointer icon is likely to become stale. Hide it to avoid confusion.
                InputManager.getInstance().setPointerIconType(PointerIcon.TYPE_NULL);
            

            // For purposes of positioning and showing the nav bar, if we have decided that it can't
            // be hidden (because of the screen aspect ratio), then take that into account.
            navVisible |= !canHideNavigationBar();
            //调用layoutNavigationBar构建导航栏
            boolean updateSysUiVisibility = layoutNavigationBar(displayFrames, uiMode, dcf,
                    navVisible, navTranslucent, navAllowedHidden, statusBarExpandedNotKeyguard);
            if (DEBUG_LAYOUT) Slog.i(TAG, "mDock rect:" + displayFrames.mDock);
            //调用layoutStatusBar构建状态栏
            updateSysUiVisibility |= layoutStatusBar(
                    displayFrames, pf, df, of, vf, dcf, sysui, isKeyguardShowing);
            if (updateSysUiVisibility) 
                updateSystemUiVisibilityLw();
            
        
        layoutScreenDecorWindows(displayFrames, pf, df, dcf);

        if (displayFrames.mDisplayCutoutSafe.top > displayFrames.mUnrestricted.top) 
            // Make sure that the zone we're avoiding for the cutout is at least as tall as the
            // status bar; otherwise fullscreen apps will end up cutting halfway into the status
            // bar.
            displayFrames.mDisplayCutoutSafe.top = Math.max(displayFrames.mDisplayCutoutSafe.top,
                    displayFrames.mStable.top);
        
    


beginLayoutLw方法中首先会判断NavigationBar是否可见以及是否半透明,然后调用layoutNavigationBar方法和layoutStatusBar方法构建NavigationBar和StatusBar所对应的视图窗口区域坐标。

三、导航栏占位视图窗口区域坐标对象的构建

构建导航栏视图窗口区域坐标的layoutNavigationBar方法r如下所示。

public class PhoneWindowManager implements WindowManagerPolicy 
	//导航栏视图对应的窗口状态
    WindowState mNavigationBar = null;
    //导航栏视图对应的窗口区域坐标
    static final Rect mTmpNavigationFrame = new Rect();
    //导航栏视图控制器
    private final BarController mNavigationBarController = new BarController("NavigationBar",
            View.NAVIGATION_BAR_TRANSIENT,
            View.NAVIGATION_BAR_UNHIDE,
            View.NAVIGATION_BAR_TRANSLUCENT,
            StatusBarManager.WINDOW_NAVIGATION_BAR,
            FLAG_TRANSLUCENT_NAVIGATION,
            View.NAVIGATION_BAR_TRANSPARENT);
    
    //构建导航栏视图对应的窗口区域坐标
    private boolean layoutNavigationBar(DisplayFrames displayFrames, int uiMode, Rect dcf,
            boolean navVisible, boolean navTranslucent, boolean navAllowedHidden,
            boolean statusBarExpandedNotKeyguard) 
        if (mNavigationBar == null) 
            return false;
        
        boolean transientNavBarShowing = mNavigationBarController.isTransientShowing();
        //根据屏幕旋转角度,我们需要为导航栏视图设置相对应的位置和大小
        final int rotation = displayFrames.mRotation;
        final int displayHeight = displayFrames.mDisplayHeight;
        final int displayWidth = displayFrames.mDisplayWidth;
        final Rect dockFrame = displayFrames.mDock;
        //获取导航栏的位置
        mNavigationBarPosition = navigationBarPosition(displayWidth, displayHeight, rotation);
        //cutoutSafeUnrestricted是安全的窗口(Android9针对刘海平新增的),当没有Overscan的时候与mUnrestricted相同
        //即cutoutSafeUnrestricted.bottom的值与DisplayFrames.mDisplayHeight值相同。
        final Rect cutoutSafeUnrestricted = mTmpRect;
        cutoutSafeUnrestricted.set(displayFrames.mUnrestricted);
        cutoutSafeUnrestricted.intersectUnchecked(displayFrames.mDisplayCutoutSafe);

        //导航栏在底部
        if (mNavigationBarPosition == NAV_BAR_BOTTOM) 
            //计算导航栏的左上角的Y坐标
            final int top = cutoutSafeUnrestricted.bottom - getNavigationBarHeight(rotation, uiMode);
            //mTmpNavigationFrame就是NavigationBar所对应的窗口区域。
            mTmpNavigationFrame.set(0, top, displayWidth, displayFrames.mUnrestricted.bottom);
            //mStable对应应用窗口的显示区域,mStableFullscreen对应应用窗口全屏的显示区域,这里的设置使得应用窗口正常状态和全屏的时候都在导航栏的上方
            displayFrames.mStable.bottom = displayFrames.mStableFullscreen.bottom = top;
            if (transientNavBarShowing) 
                mNavigationBarController.setBarShowingLw(true);
             else if (navVisible) 
                //如果NavigationBar可见的话,更新dockFrame、mRestricted、mRestrictedOverscan的bottom值
                mNavigationBarController.setBarShowingLw(true);
                dockFrame.bottom = displayFrames.mRestricted.bottom = displayFrames.mRestrictedOverscan.bottom = top;
             else 
                // We currently want to hide the navigation UI - unless we expanded the status bar.
                mNavigationBarController.setBarShowingLw(statusBarExpandedNotKeyguard);
            
            if (navVisible && !navTranslucent && !navAllowedHidden
                    && !mNavigationBar.isAnimatingLw()
                    && !mNavigationBarController.wasRecentlyTranslucent()) 
                // If the opaque nav bar is currently requested to be visible and not in the process
                // of animating on or off, then we can tell the app that it is covered by it.
                displayFrames.mSystem.bottom = top;
            
         else if (mNavigationBarPosition == NAV_BAR_RIGHT) 
            ...代码省略...
         else if (mNavigationBarPosition == NAV_BAR_LEFT) 
            ...代码省略...
        

        //使用dockFrame的参数去更新mCurrent,mVoiceContent,mContent
        displayFrames.mCurrent.set(dockFrame);
        displayFrames.mVoiceContent.set(dockFrame);
        displayFrames.mContent.set(dockFrame);
        mStatusBarLayer = mNavigationBar.getSurfaceLayer();
        //计算NavigationBar视图所对应的窗口坐标对应contentFrame的大小
        mNavigationBar.computeFrameLw(mTmpNavigationFrame, mTmpNavigationFrame,
                mTmpNavigationFrame, displayFrames.mDisplayCutoutSafe, mTmpNavigationFrame, dcf,
                mTmpNavigationFrame, displayFramesAndroid 9.0 AAudio源码分析

Android 9.0 AAudio源码分析

Android 9.0 AAudio源码分析

Android 9.0 AAudio源码分析

Android 系统源码中添加 androidx 依赖

Android 9.0 SQLiteCantOpenDatabaseException SQLITE_CANTOPEN(不支持WAL模式)源码分析定位