Android 9系统源码_SystemUI(十)SystemUIVisibility属性

前言

在Android系统中,很多应用都需要根据具体情况来控制状态栏和导航栏的显示和隐藏,又或者将状态栏透明,实现诸如沉浸式、全面屏灯效果,而要实现这些效果,都离不开SystemUIVisibility属性。由于SystemUIVisibilityy属性主要用来控制系统状态栏和导航栏的行为,而状态栏和导航栏都属于SystemUI模块的StatusBar,所以SystemUIVisibility属性的消费者肯定包含StatusBar。另外当状态栏和导航栏发生变化的时候,窗口的布局一般也会跟着发生变化,这就意味着窗口管理者PhoneWindowManager肯定也要消费SystemUIVisibility属性。本篇文章我们就来具体分析一下和这个属性有关的代码。

一、SystemUIVisibility属性常见常见取值

1、为窗口设置SystemUIVisibility属性的方式有两种,一种是直接在窗口的WindowManager.LayoutParams对象的systemUiVisibility属性上进行设置,并通过WindowManager.updateViewLayout()方法使其生效。

frameworks/base/core/java/android/view/WindowManager.java

public interface WindowManager extends ViewManager {
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
    	//隐藏窗口的所有装饰,比如状态栏和导航栏
        public static final int FLAG_FULLSCREEN      = 0x00000400;

		//控制窗口状态栏、导航栏的显示和隐藏
        public int systemUiVisibility;
    }
}    

2、另一种是在一个已经显示在窗口上的控件中调用setSystemUiVisibility方法,传入如下属性。

frameworks/base/core/java/android/view/View.java

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
//隐藏导航栏
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
//隐藏状态栏
public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
}

这种方式最终影响的其实是窗口的WindowManager.LayoutParams对象的subtreeSystemUiVisibility属性。

public interface WindowManager extends ViewManager {
    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
		//控制窗口状态栏、导航栏的显示和隐藏
        public int subtreeSystemUiVisibility;
    }
}    

窗口的状态栏导航栏显示与否,最终其实是受以上两个属性共同影响的。接下来我们具体来分析一下View的setSystemUiVisibility方法是如何生效的。

二、View的setSystemUiVisibility方法调用流程

1、View的setSystemUiVisibility方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
        
    int mSystemUiVisibility;
    protected ViewParent mParent;
    
    public void setSystemUiVisibility(int visibility) {
        if (visibility != mSystemUiVisibility) {
            mSystemUiVisibility = visibility;//保存SystemUIVisibility属性
            if (mParent != null && mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
                mParent.recomputeViewAttributes(this);//通知父控件子控件属性发生了变化
            }
        }
    }
}

setSystemUiVisibility方法首先将属性赋值给mSystemUiVisibility,然后会调用父控件的recomputeViewAttributes方法,通知父控件子控件属性发生了变化。ViewParent是一个接口,在Android中有两个类实现了这个接口,它们分别是ViewGroup和ViewRootImpl。

2、ViewGroup和ViewRootImpl和recomputeViewAttributes方法相关的代码如下所示。

frameworks/base/core/java/android/view/View.java

public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    @Override
    public void recomputeViewAttributes(View child) {
        if (mAttachInfo != null && !mAttachInfo.mRecomputeGlobalAttributes) {
            ViewParent parent = mParent;
            if (parent != null) parent.recomputeViewAttributes(this);
        }
    }
}

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
    
    final View.AttachInfo mAttachInfo;
    @Override
    public void recomputeViewAttributes(View child) {
        checkThread();//检测线程是不是UI线程
        if (mView == child) {
            mAttachInfo.mRecomputeGlobalAttributes = true;//标记需要重新计算本地属性
            if (!mWillDrawSoon) {
                scheduleTraversals();//进一步调用scheduleTraversals方法。
            }
        }
    }
}

结合Android 9.0系统源码_窗口管理(二)WindowManager对窗口的管理过程,我们知道包括Activity的跟布局DecorView在内的任何View,WindowManager在将它添加到窗口上的过程中,最终都会创建一个ViewRootImpl,并将View设置给ViewRootImpl,这样根View的父类就变成了ViewRootImpl。这就意味着不管任何子View调用recomputeViewAttributes方法,最终所触发的都是ViewRootImpl的recomputeViewAttributes,而ViewRootImpl会进一步调用scheduleTraversals方法。

3、ViewRootImpl和scheduleTraversals方法相关的代码如下所示。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
        
     final Choreographer mChoreographer;//编舞者
    final TraversalRunnable mTraversalRunnable = new TraversalRunnable();//回调对象

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法
            mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
    
   final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();//继续执行doTraversal
        }
    }

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

            if (mProfile) {
                Debug.startMethodTracing("ViewAncestor");
            }
			//执行performTraversals
            performTraversals();

            if (mProfile) {
                Debug.stopMethodTracing();
                mProfile = false;
            }
        }
    }
}

scheduleTraversals方法会为编舞者对象设置回调,最终会等待系统垂直刷新同步信号,回调TraversalRunnable对象的run方法,该方法会调用doTraversal方法,然后进一步调用performTraversals方法。

4、ViewRootImpl的performTraversals方法代码逻辑非常多,这里只列出了我们需要关注的代码。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
        
    final IWindowSession mWindowSession;//和WMS通信的Binder对象,具体为Session对象
	public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性
    final View.AttachInfo mAttachInfo;//控件信息
    
    private void performTraversals() {
        final View host = mView;
        if (host == null || !mAdded) {
            return;
        }
        if (mWaitForBlastSyncComplete) {
            mRequestedTraverseWhilePaused = true;
            return;
        }
        mIsInTraversal = true;
        mWillDrawSoon = true;
        boolean windowSizeMayChange = false;
        WindowManager.LayoutParams lp = mWindowAttributes;//将当前窗口的最新属性赋值给lp
        int desiredWindowWidth;
        int desiredWindowHeight;
        final int viewVisibility = getHostVisibility();
        final boolean viewVisibilityChanged = !mFirst
                && (mViewVisibility != viewVisibility || mNewSurfaceNeeded
                || mAppVisibilityChanged);
        mAppVisibilityChanged = false;
        final boolean viewUserVisibilityChanged = !mFirst &&
                ((mViewVisibility == View.VISIBLE) != (viewVisibility == View.VISIBLE));
        WindowManager.LayoutParams params = null;
        ...代码省略...
        //收集mView的属性,判断是否需要更新params
        if (collectViewAttributes()) {
            params = lp;
        }
        ...代码省略...
        //此方法最终会触发WindowManagerService的relayoutWindow方法
		relayoutWindow(params, viewVisibility, insetsPending);
       	...代码省略...  
       	//测量
     	performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
     	...代码省略...  
     	//布局
     	performLayout(lp, mWidth, mHeight);
     	...代码省略...  
     	//绘制
        performDraw();
        ...代码省略...  
     }

    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
        	...代码省略...         
        	//调用IWindowSession的relayout方法,该方法最终会触发WindowManagerService的relayoutWindow方法
             int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
               (int) (mView.getMeasuredWidth() * appScale + 0.5f),
               (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
               insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
               mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
               mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
               mPendingMergedConfiguration, mSurface);
     }
  }

performTraversals首先调用collectViewAttributes方法收集所有子View的属性,然后调用relayoutWindow方法,该方法最终会触发WindowManagerService的relayoutWindow方法,然后回继续调用触发View测量的performMeasure方法,触发View布局的performLayout方法和触发View绘制的performDraw方法。

三、获取最新的SystemUIVisibility属性

1、ViewRootImpl的collectViewAttributes方法是一个很关键的方法,此方法会重新计算最新的SystemUIVisibility属性。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {

	public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性
    final View.AttachInfo mAttachInfo;//控件信息
    
    private boolean collectViewAttributes() {
  		//判断是否需要重新计算本地属性
        if (mAttachInfo.mRecomputeGlobalAttributes) {
            //Log.i(mTag, "Computing view hierarchy attributes!");
            mAttachInfo.mRecomputeGlobalAttributes = false;
            boolean oldScreenOn = mAttachInfo.mKeepScreenOn;
            mAttachInfo.mKeepScreenOn = false;
            //清空已经存在的SystemUiVisibility属性
            mAttachInfo.mSystemUiVisibility = 0;
            mAttachInfo.mHasSystemUiListeners = false;
            //重新获取窗口视图mView最新的的SystemUI属性,赋值给mAttachInfo
            mView.dispatchCollectViewAttributes(mAttachInfo, 0);
         	...代码暂时省略...
        }
        return false;
    }
 }

collectViewAttributes首先会清空当前窗口视图mView已经存在的SystemUiVisibility属性,然后调用View的dispatchCollectViewAttributes方法重新获取最新的的SystemUiVisibility属性。

2、View的dispatchCollectViewAttributes方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    
    int mSystemUiVisibility;
    
    void dispatchCollectViewAttributes(AttachInfo attachInfo, int visibility) {
        performCollectViewAttributes(attachInfo, visibility);
    }
    
    void performCollectViewAttributes(AttachInfo attachInfo, int visibility) {
        if ((visibility & VISIBILITY_MASK) == VISIBLE) {
            if ((mViewFlags & KEEP_SCREEN_ON) == KEEP_SCREEN_ON) {
                attachInfo.mKeepScreenOn = true;
            }
            //将最新的systemuivisiblity赋予AttachInfo的mSystemUiVisibility 属性
            attachInfo.mSystemUiVisibility |= mSystemUiVisibility;
            //设置最新的SystemUiVisibility监听对象,如果不为空,则将AttachInfo的mHasSystemUiListeners属性设置为true。
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnSystemUiVisibilityChangeListener != null) {
                attachInfo.mHasSystemUiListeners = true;
            }
        }
    }

}    

3、接着看ViewRootImpl的collectViewAttributes方法。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {

	public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性
    final View.AttachInfo mAttachInfo;//控件信息
    
    private boolean collectViewAttributes() {
  		//判断是否需要重新计算本地属性
        if (mAttachInfo.mRecomputeGlobalAttributes) {
         	...代码省略...
            //移除被禁用的SystemUiVisibility属性
            mAttachInfo.mSystemUiVisibility &= ~mAttachInfo.mDisabledSystemUiVisibility;
            //让params引用指向mWindowAttributes对象
            WindowManager.LayoutParams params = mWindowAttributes;
            mAttachInfo.mSystemUiVisibility |= getImpliedSystemUiVisibility(params);
            if (mAttachInfo.mKeepScreenOn != oldScreenOn
                    || mAttachInfo.mSystemUiVisibility != params.subtreeSystemUiVisibility
                    || mAttachInfo.mHasSystemUiListeners != params.hasSystemUiListeners) {
                applyKeepScreenOnFlag(params);
                //将重新获取的窗口视图mView的SystemUiVisibility保存到窗口的LayoutParams属性中
                params.subtreeSystemUiVisibility = mAttachInfo.mSystemUiVisibility;
                params.hasSystemUiListeners = mAttachInfo.mHasSystemUiListeners;
                //调用View的dispatchWindowSystemUiVisiblityChanged方法
                mView.dispatchWindowSystemUiVisiblityChanged(mAttachInfo.mSystemUiVisibility);
                return true;
            }
        }
        return false;
    }
 }

在重新获得mView的SystemUiVisibility属性之后,首先会从该属性中移除被禁用的SystemUiVisibility属性,然后让params引用指向mWindowAttributes对象,并将重新获取的保存在mAttachInfo对象中的SystemUiVisibility属性保存到当前窗口的LayoutParams属性中,最后会调用当前View的dispatchWindowSystemUiVisiblityChanged方法。

4、View的dispatchWindowSystemUiVisiblityChanged方法如下所示。

public class View implements Drawable.Callback, KeyEvent.Callback,
        AccessibilityEventSource {
    @Deprecated
    public void dispatchWindowSystemUiVisiblityChanged(int visible) {
        onWindowSystemUiVisibilityChanged(visible);//调用onWindowSystemUiVisibilityChanged方法
    }
    public void onWindowSystemUiVisibilityChanged(int visible) {
    	//默认为空实现
    }
}

该方法会进一步调用onWindowSystemUiVisibilityChanged方法,onWindowSystemUiVisibilityChanged方法默认为空实现,但是如果当前mView为DecorView时则不同,DecorView实现了此方法。

四、DecorView更新状态栏和导航栏背景颜色

1、DecorView的onWindowSystemUiVisibilityChanged方法如下所示。

frameworks/base/core/java/com/android/internal/policy/DecorView.java

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {

    @Override
    public void onWindowSystemUiVisibilityChanged(int visible) {
    	//调用updateColorViews方法
        updateColorViews(null /* insets */, true /* animate */);
    }

}

onWindowSystemUiVisibilityChanged方法会调用一个updateColorViews这个关键方法。

2、updateColorViews会调用updateColorViewInt方法更新导航栏和状态栏的背景颜色。

    public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    
    WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
    	//获取窗口的SystemUIVisibility属性
        int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
        //判断窗口类型是否是输入法
        final boolean isImeWindow = mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
        //判断窗口类型不是浮动窗口和输入法,则让SystemUIVisibility属性生效
        if (!mWindow.mIsFloating || isImeWindow) {
        	//获取是否禁止窗口动画的标记
            boolean disallowAnimate = !isLaidOut();
            disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
                    & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
            mLastWindowFlags = attrs.flags;
            if (insets != null) {
                mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
                        insets.getSystemWindowInsetTop());
                mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
                        insets.getSystemWindowInsetBottom());
                mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
                        insets.getSystemWindowInsetRight());
                mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
                        insets.getSystemWindowInsetLeft());

                // Don't animate if the presence of stable insets has changed, because that
                // indicates that the window was either just added and received them for the
                // first time, or the window size or position has changed.
                boolean hasTopStableInset = insets.getStableInsetTop() != 0;
                disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
                mLastHasTopStableInset = hasTopStableInset;

                boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
                disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
                mLastHasBottomStableInset = hasBottomStableInset;

                boolean hasRightStableInset = insets.getStableInsetRight() != 0;
                disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
                mLastHasRightStableInset = hasRightStableInset;

                boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
                disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
                mLastHasLeftStableInset = hasLeftStableInset;

                mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
            }

            boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
            boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
            int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
            //更新导航栏颜色,mNavigationColorViewState为导航栏的相关筛选条件
            updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
                    mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize/*导航栏高度*/,
                    navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
                    0 /* sideInset */, animate && !disallowAnimate, false /* force */);

            boolean statusBarNeedsRightInset = navBarToRightEdge
                    && mNavigationColorViewState.present;
            boolean statusBarNeedsLeftInset = navBarToLeftEdge
                    && mNavigationColorViewState.present;
            int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
                    : statusBarNeedsLeftInset ? mLastLeftInset : 0;
            //更新状态栏颜色,mStatusColorViewState为状态栏的相关筛选条件
            updateColorViewInt(mStatusColorViewState, sysUiVisibility,
                    calculateStatusBarColor(), 0, mLastTopInset/*状态栏高度*/,
                    false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
                    animate && !disallowAnimate,
                    mForceWindowDrawsStatusBarBackground);
        }
    		...代码省略...
    }
        
}

关于DecorView更新状态栏、导航栏背景颜色的具体过程,请参考Android 9.0系统源码_SystemUI(八)PhoneWindow更新状态栏和导航栏背景颜色的流程解析。

五、WindowManagerService的relayoutWindow方法

1、重新回到第二节第4步ViewRootImpl的performTraversals方法中。

public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
        AttachedSurfaceControl {
        
    final IWindowSession mWindowSession;//和WMS通信的Binder对象,具体为Session对象
	public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();//窗口属性
    final View.AttachInfo mAttachInfo;//控件信息
    
    private void performTraversals() {
        ...代码省略...
        //收集mView的属性,判断是否需要更新params
        if (collectViewAttributes()) {
            params = lp;
        }
        ...代码省略...
        //此方法最终会触发WindowManagerService的relayoutWindow方法
		relayoutWindow(params, viewVisibility, insetsPending);
       	...代码省略...  
       	//测量
     	performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
     	...代码省略...  
     	//布局
     	performLayout(lp, mWidth, mHeight);
     	...代码省略...  
     	//绘制
        performDraw();
        ...代码省略...  
     }

    private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
            boolean insetsPending) throws RemoteException {
        	...代码省略...         
        	//调用IWindowSession的relayout方法,该方法最终会触发WindowManagerService的relayoutWindow方法
             int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,
               (int) (mView.getMeasuredWidth() * appScale + 0.5f),
               (int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,
               insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,
               mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
               mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,
               mPendingMergedConfiguration, mSurface);
     }
  }

在调用collectViewAttributes获取最新的systemUIVisibiliy属性之后,会调用relayoutWindow方法,该方法进一步调用IWindowSession的relayout方法,IWindowSession的具体实现类为Session。

2、Session的relayout方法如下所示。

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

class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {

    final WindowManagerService mService;
    
    @Override
    public int relayout(IWindow window, WindowManager.LayoutParams attrs,
            int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
            ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
            SurfaceControl outSurfaceControl, InsetsState outInsetsState,
            InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
        int res = mService.relayoutWindow(this, window, attrs,
                requestedWidth, requestedHeight, viewFlags, flags, frameNumber,
                outFrames, mergedConfiguration, outSurfaceControl, outInsetsState,
                outActiveControls, outSurfaceSize);
        return res;
    }
}

relayout方法会进一步调用WindowManagerService的relayoutWindow方法。

3、WindowManagerService的relayoutWindow方法如下所示。

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

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
        
    public int relayoutWindow(Session session, IWindow client, int seq, LayoutParams attrs/**窗口属性**/,
            int requestedWidth, int requestedHeight, int viewVisibility/**根View控件是否可见**/, int flags,
            long frameNumber, Rect outFrame, Rect outOverscanInsets, Rect outContentInsets,
            Rect outVisibleInsets, Rect outStableInsets, Rect outOutsets, Rect outBackdropFrame,
            DisplayCutout.ParcelableWrapper outCutout, MergedConfiguration mergedConfiguration,
            Surface outSurface) {
        int result = 0;
        boolean configChanged;
        //是否有状态栏的使用权限
        final boolean hasStatusBarPermission =
                mContext.checkCallingOrSelfPermission(permission.STATUS_BAR)
                        == PackageManager.PERMISSION_GRANTED;
        //是否有状态栏服务的使用权限
        final boolean hasStatusBarServicePermission =
                mContext.checkCallingOrSelfPermission(permission.STATUS_BAR_SERVICE)
                        == PackageManager.PERMISSION_GRANTED;

        long origId = Binder.clearCallingIdentity();
        final int displayId;
        synchronized(mWindowMap) {
            //获取当前要操作的窗口对象
            WindowState win = windowForClientLocked(session, client, false);
            if (win == null) {
                return 0;
            }
            //获取窗口所属的屏幕设备id
            displayId = win.getDisplayId();
            //窗口动画
            WindowStateAnimator winAnimator = win.mWinAnimator;
            if (viewVisibility != View.GONE) {
                win.setRequestedSize(requestedWidth, requestedHeight);
            }

            win.setFrameNumber(frameNumber);
            int attrChanges = 0;
            int flagChanges = 0;
            if (attrs != null) {
                //如果窗口属性不为空,这里会对窗口的相关属性进行一次预处理
                mPolicy.adjustWindowParamsLw(win, attrs, hasStatusBarServicePermission);
                // if they don't have the permission, mask out the status bar bits
                if (seq == win.mSeq) {
                    int systemUiVisibility = attrs.systemUiVisibility
                            | attrs.subtreeSystemUiVisibility;
                    if ((systemUiVisibility & DISABLE_MASK) != 0) {
                        if (!hasStatusBarPermission) {
                            systemUiVisibility &= ~DISABLE_MASK;
                        }
                    }
                    win.mSystemUiVisibility = systemUiVisibility;
                }
                if (win.mAttrs.type != attrs.type) {
                    throw new IllegalArgumentException(
                            "Window type can not be changed after the window is added.");
                }

                // Odd choice but less odd than embedding in copyFrom()
                if ((attrs.privateFlags & WindowManager.LayoutParams.PRIVATE_FLAG_PRESERVE_GEOMETRY)
                        != 0) {
                    attrs.x = win.mAttrs.x;
                    attrs.y = win.mAttrs.y;
                    attrs.width = win.mAttrs.width;
                    attrs.height = win.mAttrs.height;
                }
                //检测窗口标记和样式是否发生了变化
                flagChanges = win.mAttrs.flags ^= attrs.flags;
                attrChanges = win.mAttrs.copyFrom(attrs);
                if ((attrChanges & (WindowManager.LayoutParams.LAYOUT_CHANGED
                        | WindowManager.LayoutParams.SYSTEM_UI_VISIBILITY_CHANGED)) != 0) {
                    win.mLayoutNeeded = true;
                }
                if (win.mAppToken != null && ((flagChanges & FLAG_SHOW_WHEN_LOCKED) != 0
                        || (flagChanges & FLAG_DISMISS_KEYGUARD) != 0)) {
                    win.mAppToken.checkKeyguardFlagsChanged();
                }
                if (((attrChanges & LayoutParams.ACCESSIBILITY_TITLE_CHANGED) != 0)
                        && (mAccessibilityController != null)
                        && (win.getDisplayId() == DEFAULT_DISPLAY)) {
                    // No move or resize, but the controller checks for title changes as well
                    mAccessibilityController.onSomeWindowResizedOrMovedLocked();
                }

                if ((flagChanges & PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS) != 0) {
                    updateNonSystemOverlayWindowsVisibilityIfNeeded(
                            win, win.mWinAnimator.getShown());
                }
            }
            ...代码省略... 
            if (focusMayChange) {
                //System.out.println("Focus may change: " + win.mAttrs.getTitle());
                if (updateFocusedWindowLocked(UPDATE_FOCUS_WILL_PLACE_SURFACES,
                        false /*updateInputWindows*/)) {
                    imMayMove = false;
                }
                //System.out.println("Relayout " + win + ": focus=" + mCurrentFocus);
            }
            ...代码省略...           
	}
}

relayoutWindow方法会调用一个关键方法updateFocusedWindowLocked。

4、WindowManagerService的updateFocusedWindowLocked方法如下所示。

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
   
    //窗口管理策略的接口类WindowManagerPolicy(WMP),它用来定义一个窗口策略所要遵循的通用规范。
    final WindowManagerPolicy mPolicy;
    //根窗口
    RootWindowContainer mRoot;
    
    private WindowManagerService(Context context, InputManagerService inputManager,
	            boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore,
	            WindowManagerPolicy policy) {
	            ...代码省略...
	            mPolicy = policy;
		        mRoot = new RootWindowContainer(this);
	            ...代码省略...
	}
    
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        //获取当前最新的焦点窗口
        WindowState newFocus = mRoot.computeFocusedWindow();
        if (mCurrentFocus != newFocus) {//如果窗口焦点发生了变化
            Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
            // This check makes sure that we don't already have the focus
            // change message pending.
            mH.removeMessages(H.REPORT_FOCUS_CHANGE);
            mH.sendEmptyMessage(H.REPORT_FOCUS_CHANGE);
            // TODO(multidisplay): Focused windows on default display only.
            final DisplayContent displayContent = getDefaultDisplayContentLocked();
            boolean imWindowChanged = false;
            if (mInputMethodWindow != null) {//如果输入法窗口不为空
                final WindowState prevTarget = mInputMethodTarget;
                final WindowState newTarget =
                        displayContent.computeImeTarget(true /* updateImeTarget*/);

                imWindowChanged = prevTarget != newTarget;

                if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
                        && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                    final int prevImeAnimLayer = mInputMethodWindow.mWinAnimator.mAnimLayer;
                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                    imWindowChanged |=
                            prevImeAnimLayer != mInputMethodWindow.mWinAnimator.mAnimLayer;
                }
            }

            if (imWindowChanged) {//输入法窗口发生了变化
                mWindowsChanged = true;
                displayContent.setLayoutNeeded();
                newFocus = mRoot.computeFocusedWindow();
            }

            if (DEBUG_FOCUS_LIGHT || localLOGV) Slog.v(TAG_WM, "Changing focus from " +
                    mCurrentFocus + " to " + newFocus + " Callers=" + Debug.getCallers(4));
            final WindowState oldFocus = mCurrentFocus;
            mCurrentFocus = newFocus;
            mLosingFocus.remove(newFocus);

            if (mCurrentFocus != null) {
                mWinAddedSinceNullFocus.clear();
                mWinRemovedSinceNullFocus.clear();
            }
            //调用WindowManagerPolicy的focusChangedLw方法
            int focusChanged = mPolicy.focusChangedLw(oldFocus, newFocus);

            if (imWindowChanged && oldFocus != mInputMethodWindow) {
                // Focus of the input method window changed. Perform layout if needed.
                if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                    displayContent.performLayout(true /*initial*/,  updateInputWindows);
                    focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
                } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                    // Client will do the layout, but we need to assign layers
                    // for handleNewWindowLocked() below.
                    displayContent.assignWindowLayers(false /* setLayoutNeeded */);
                }
            }

            if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
                // The change in focus caused us to need to do a layout.  Okay.
                displayContent.setLayoutNeeded();
                if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                    displayContent.performLayout(true /*initial*/, updateInputWindows);
                }
            }

            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
                // If we defer assigning layers, then the caller is responsible for
                // doing this part.
                mInputMonitor.setInputFocusLw(mCurrentFocus, updateInputWindows);
            }

            displayContent.adjustForImeIfNeeded();

            // We may need to schedule some toast windows to be removed. The toasts for an app that
            // does not have input focus are removed within a timeout to prevent apps to redress
            // other apps' UI.
            displayContent.scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);

            Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
            return true;
        }
        return false;
    }

}

updateFocusedWindowLocked首先是获取最新的焦点窗口,之后还会判断当前窗口焦点是否发生了变化,如果发生了变化,则会调用WindowManagerPolicy的focusChangedLw方法。

六、PhoneWindowManager的updateSystemUiVisibilityLw方法

1、WindowManagerPolicy是一个抽象接口。

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

public interface WindowManagerPolicy extends WindowManagerPolicyConstants {
    /**
     * 一个新的窗口持有了焦点
     */
    public int focusChangedLw(WindowState lastFocus, WindowState newFocus);
}

2、结合Android 9.0系统源码_窗口管理(一)WindowManagerService的启动流程这篇文章我们可以知道,SystemServer在创建WindowManagerService对象的时候,将PhoneWindowManager对象实例赋值给了mPolicy。

frameworks/base/service/java/com/android/server/SystemServer.java

public final class SystemServer {	
	  private void startOtherServices() {
			 ...代码省略...
	          wm = WindowManagerService.main(context, inputManager,
	                 mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
	                 !mFirstBoot, mOnlyCore, new PhoneWindowManager());
	          ...代码省略...
	 }
 }

3、来看下PhoneWindowManager是如何实现focusChangedLw方法的。

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

public class PhoneWindowManager implements WindowManagerPolicy {
    @Override
    public int focusChangedLw(WindowState lastFocus, WindowState newFocus) {
        mFocusedWindow = newFocus;
        if ((updateSystemUiVisibilityLw()&SYSTEM_UI_CHANGING_LAYOUT) != 0) {
            // If the navigation bar has been hidden or shown, we need to do another
            // layout pass to update that window.
            return FINISH_LAYOUT_REDO_LAYOUT;
        }
        return 0;
    }
    
    //更新窗口的SystemUiVisibility属性参数
    private int updateSystemUiVisibilityLw() {
        //获取当前的焦点窗口
        WindowState winCandidate = mFocusedWindow != null ? mFocusedWindow
                : mTopFullscreenOpaqueWindowState;
        //如果不存在焦点窗口则直接返回
        if (winCandidate == null) {
            return 0;
        }
        if (winCandidate.getAttrs().token == mImmersiveModeConfirmation.getWindowToken()) {
            winCandidate = isStatusBarKeyguard() ? mStatusBar : mTopFullscreenOpaqueWindowState;
            if (winCandidate == null) {
                return 0;
            }
        }
        final WindowState win = winCandidate;
        if ((win.getAttrs().privateFlags & PRIVATE_FLAG_KEYGUARD) != 0 && mKeyguardOccluded) {
            return 0;
        }

        int tmpVisibility = PolicyControl.getSystemUiVisibility(win, null)
                & ~mResettingSystemUiFlags
                & ~mForceClearedSystemUiFlags;
        if (mForcingShowNavBar && win.getSurfaceLayer() < mForcingShowNavBarLayer) {
            tmpVisibility &= ~PolicyControl.adjustClearableFlags(win, View.SYSTEM_UI_CLEARABLE_FLAGS);
        }
        //获取和SystemUIVisibility相关的各种窗口参数
        final int fullscreenVisibility = updateLightStatusBarLw(0 /* vis */,
                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState);
        final int dockedVisibility = updateLightStatusBarLw(0 /* vis */,
                mTopDockedOpaqueWindowState, mTopDockedOpaqueOrDimmingWindowState);
        mWindowManagerFuncs.getStackBounds(
                WINDOWING_MODE_UNDEFINED, ACTIVITY_TYPE_HOME, mNonDockedStackBounds);
        mWindowManagerFuncs.getStackBounds(
                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, mDockedStackBounds);
        final int visibility = updateSystemBarsLw(win, mLastSystemUiFlags, tmpVisibility);
        final int diff = visibility ^ mLastSystemUiFlags;
        final int fullscreenDiff = fullscreenVisibility ^ mLastFullscreenStackSysUiFlags;
        final int dockedDiff = dockedVisibility ^ mLastDockedStackSysUiFlags;
        final boolean needsMenu = win.getNeedsMenuLw(mTopFullscreenOpaqueWindowState);
        if (diff == 0 && fullscreenDiff == 0 && dockedDiff == 0 && mLastFocusNeedsMenu == needsMenu
                && mFocusedApp == win.getAppToken()
                && mLastNonDockedStackBounds.equals(mNonDockedStackBounds)
                && mLastDockedStackBounds.equals(mDockedStackBounds)) {
            return 0;
        }
        mLastSystemUiFlags = visibility;
        mLastFullscreenStackSysUiFlags = fullscreenVisibility;
        mLastDockedStackSysUiFlags = dockedVisibility;
        mLastFocusNeedsMenu = needsMenu;
        mFocusedApp = win.getAppToken();
        final Rect fullscreenStackBounds = new Rect(mNonDockedStackBounds);
        final Rect dockedStackBounds = new Rect(mDockedStackBounds);
        mHandler.post(new Runnable() {
                @Override
                public void run() {
                    StatusBarManagerInternal statusbar = getStatusBarManagerInternal();
                    if (statusbar != null) {
                        //最终会触发状态栏管理服务StatusBarManagerService的setSystemUiVisibility方法,
                        //通知状态栏和底部栏进行样式调整
                        statusbar.setSystemUiVisibility(visibility, fullscreenVisibility,
                                dockedVisibility, 0xffffffff, fullscreenStackBounds,
                                dockedStackBounds, win.toString());
                        statusbar.topAppWindowChanged(needsMenu);
                    }
                }
            });
        return diff;
    }
}

PhoneWindowManager的focusChangedLw方法直接调用了updateSystemUiVisibilityLw方法,此方法会对窗口的SystemUiVisibility属性做一些处理,最终调用状态栏管理服务StatusBarManagerService的setSystemUiVisibility方法,通知状态栏和底部栏进行样式调整。

4、updateSystemUiVisibilityLw方法中有用到对SystemUIVisibility属性做处理的几个关键方法:updateLightStatusBarLw、chooseNavigationColorWindowLw、updateLightNavigationBarLw和updateSystemBarsLw,这里一并贴出,有兴趣的可以看下。

public class PhoneWindowManager implements WindowManagerPolicy {
    private int updateLightStatusBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming) {
        final boolean onKeyguard = isStatusBarKeyguard() && !mKeyguardOccluded;
        final WindowState statusColorWin = onKeyguard ? mStatusBar : opaqueOrDimming;
        if (statusColorWin != null && (statusColorWin == opaque || onKeyguard)) {
            // If the top fullscreen-or-dimming window is also the top fullscreen, respect
            // its light flag.
            vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            vis |= PolicyControl.getSystemUiVisibility(statusColorWin, null)
                    & View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
        } else if (statusColorWin != null && statusColorWin.isDimming()) {
            // Otherwise if it's dimming, clear the light flag.
            vis &= ~View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
        }
        return vis;
    }

    @VisibleForTesting
    @Nullable
    static WindowState chooseNavigationColorWindowLw(WindowState opaque,
            WindowState opaqueOrDimming, WindowState imeWindow,
            @NavigationBarPosition int navBarPosition) {
        // If the IME window is visible and FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS is set, then IME
        // window can be navigation color window.
        final boolean imeWindowCanNavColorWindow = imeWindow != null
                && imeWindow.isVisibleLw()
                && navBarPosition == NAV_BAR_BOTTOM
                && (PolicyControl.getWindowFlags(imeWindow, null)
                & WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;

        if (opaque != null && opaqueOrDimming == opaque) {
            // If the top fullscreen-or-dimming window is also the top fullscreen, respect it
            // unless IME window is also eligible, since currently the IME window is always show
            // above the opaque fullscreen app window, regardless of the IME target window.
            // TODO(b/31559891): Maybe we need to revisit this condition once b/31559891 is fixed.
            return imeWindowCanNavColorWindow ? imeWindow : opaque;
        }

        if (opaqueOrDimming == null || !opaqueOrDimming.isDimming()) {
            // No dimming window is involved. Determine the result only with the IME window.
            return imeWindowCanNavColorWindow ? imeWindow : null;
        }

        if (!imeWindowCanNavColorWindow) {
            // No IME window is involved. Determine the result only with opaqueOrDimming.
            return opaqueOrDimming;
        }

        // The IME window and the dimming window are competing.  Check if the dimming window can be
        // IME target or not.
        if (LayoutParams.mayUseInputMethod(PolicyControl.getWindowFlags(opaqueOrDimming, null))) {
            // The IME window is above the dimming window.
            return imeWindow;
        } else {
            // The dimming window is above the IME window.
            return opaqueOrDimming;
        }
    }

    @VisibleForTesting
    static int updateLightNavigationBarLw(int vis, WindowState opaque, WindowState opaqueOrDimming,
            WindowState imeWindow, WindowState navColorWin) {

        if (navColorWin != null) {
            if (navColorWin == imeWindow || navColorWin == opaque) {
                // Respect the light flag.
                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
                vis |= PolicyControl.getSystemUiVisibility(navColorWin, null)
                        & View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
            } else if (navColorWin == opaqueOrDimming && navColorWin.isDimming()) {
                // Clear the light flag for dimming window.
                vis &= ~View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
            }
        }
        return vis;
    }
    
    private int updateSystemBarsLw(WindowState win, int oldVis, int vis) {
        final boolean dockedStackVisible =
                mWindowManagerInternal.isStackVisible(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
        final boolean freeformStackVisible =
                mWindowManagerInternal.isStackVisible(WINDOWING_MODE_FREEFORM);
        final boolean resizing = mWindowManagerInternal.isDockedDividerResizing();

        // We need to force system bars when the docked stack is visible, when the freeform stack
        // is visible but also when we are resizing for the transitions when docked stack
        // visibility changes.
        mForceShowSystemBars = dockedStackVisible || freeformStackVisible || resizing;
        final boolean forceOpaqueStatusBar = mForceShowSystemBars && !mForceStatusBarFromKeyguard;

        // apply translucent bar vis flags
        WindowState fullscreenTransWin = isStatusBarKeyguard() && !mKeyguardOccluded
                ? mStatusBar
                : mTopFullscreenOpaqueWindowState;
        vis = mStatusBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
        vis = mNavigationBarController.applyTranslucentFlagLw(fullscreenTransWin, vis, oldVis);
        final int dockedVis = mStatusBarController.applyTranslucentFlagLw(
                mTopDockedOpaqueWindowState, 0, 0);

        final boolean fullscreenDrawsStatusBarBackground =
                drawsStatusBarBackground(vis, mTopFullscreenOpaqueWindowState);
        final boolean dockedDrawsStatusBarBackground =
                drawsStatusBarBackground(dockedVis, mTopDockedOpaqueWindowState);

        // prevent status bar interaction from clearing certain flags
        int type = win.getAttrs().type;
        boolean statusBarHasFocus = type == TYPE_STATUS_BAR;
        if (statusBarHasFocus && !isStatusBarKeyguard()) {
            int flags = View.SYSTEM_UI_FLAG_FULLSCREEN
                    | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                    | View.SYSTEM_UI_FLAG_IMMERSIVE
                    | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                    | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
            if (mKeyguardOccluded) {
                flags |= View.STATUS_BAR_TRANSLUCENT | View.NAVIGATION_BAR_TRANSLUCENT;
            }
            vis = (vis & ~flags) | (oldVis & flags);
        }

        if (fullscreenDrawsStatusBarBackground && dockedDrawsStatusBarBackground) {
            vis |= View.STATUS_BAR_TRANSPARENT;
            vis &= ~View.STATUS_BAR_TRANSLUCENT;
        } else if ((!areTranslucentBarsAllowed() && fullscreenTransWin != mStatusBar)
                || forceOpaqueStatusBar) {
            vis &= ~(View.STATUS_BAR_TRANSLUCENT | View.STATUS_BAR_TRANSPARENT);
        }

        vis = configureNavBarOpacity(vis, dockedStackVisible, freeformStackVisible, resizing);

        // update status bar
        boolean immersiveSticky =
                (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
        final boolean hideStatusBarWM =
                mTopFullscreenOpaqueWindowState != null
                && (PolicyControl.getWindowFlags(mTopFullscreenOpaqueWindowState, null)
                        & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0;
        final boolean hideStatusBarSysui =
                (vis & View.SYSTEM_UI_FLAG_FULLSCREEN) != 0;
        final boolean hideNavBarSysui =
                (vis & View.SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;

        final boolean transientStatusBarAllowed = mStatusBar != null
                && (statusBarHasFocus || (!mForceShowSystemBars
                        && (hideStatusBarWM || (hideStatusBarSysui && immersiveSticky))));

        final boolean transientNavBarAllowed = mNavigationBar != null
                && !mForceShowSystemBars && hideNavBarSysui && immersiveSticky;

        final long now = SystemClock.uptimeMillis();
        final boolean pendingPanic = mPendingPanicGestureUptime != 0
                && now - mPendingPanicGestureUptime <= PANIC_GESTURE_EXPIRATION;
        if (pendingPanic && hideNavBarSysui && !isStatusBarKeyguard() && mKeyguardDrawComplete) {
            // The user performed the panic gesture recently, we're about to hide the bars,
            // we're no longer on the Keyguard and the screen is ready. We can now request the bars.
            mPendingPanicGestureUptime = 0;
            mStatusBarController.showTransient();
            if (!isNavBarEmpty(vis)) {
                mNavigationBarController.showTransient();
            }
        }

        final boolean denyTransientStatus = mStatusBarController.isTransientShowRequested()
                && !transientStatusBarAllowed && hideStatusBarSysui;
        final boolean denyTransientNav = mNavigationBarController.isTransientShowRequested()
                && !transientNavBarAllowed;
        if (denyTransientStatus || denyTransientNav || mForceShowSystemBars) {
            // clear the clearable flags instead
            clearClearableFlagsLw();
            vis &= ~View.SYSTEM_UI_CLEARABLE_FLAGS;
        }

        final boolean immersive = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE) != 0;
        immersiveSticky = (vis & View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY) != 0;
        final boolean navAllowedHidden = immersive || immersiveSticky;

        if (hideNavBarSysui && !navAllowedHidden
                && getWindowLayerLw(win) > getWindowLayerFromTypeLw(TYPE_INPUT_CONSUMER)) {
            // We can't hide the navbar from this window otherwise the input consumer would not get
            // the input events.
            vis = (vis & ~View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
        }

        vis = mStatusBarController.updateVisibilityLw(transientStatusBarAllowed, oldVis, vis);

        // update navigation bar
        boolean oldImmersiveMode = isImmersiveMode(oldVis);
        boolean newImmersiveMode = isImmersiveMode(vis);
        if (win != null && oldImmersiveMode != newImmersiveMode) {
            final String pkg = win.getOwningPackage();
            mImmersiveModeConfirmation.immersiveModeChangedLw(pkg, newImmersiveMode,
                    isUserSetupComplete(), isNavBarEmpty(win.getSystemUiVisibility()));
        }

        vis = mNavigationBarController.updateVisibilityLw(transientNavBarAllowed, oldVis, vis);

        final WindowState navColorWin = chooseNavigationColorWindowLw(
                mTopFullscreenOpaqueWindowState, mTopFullscreenOpaqueOrDimmingWindowState,
                mWindowManagerFuncs.getInputMethodWindowLw(), mNavigationBarPosition);
        vis = updateLightNavigationBarLw(vis, mTopFullscreenOpaqueWindowState,
                mTopFullscreenOpaqueOrDimmingWindowState,
                mWindowManagerFuncs.getInputMethodWindowLw(), navColorWin);

        return vis;
    }
}

七、状态栏管理服务StatusBarManagerService发送SystemUIVisibility属性

1、在第六节第2步的最后,PhoneWindowManager先是调用getStatusBarManagerInternal方法获取StatusBarManagerInternal的对象实例,然后再调用该对象的setSystemUiVisibility方法,PhoneWindowManager的getStatusBarManagerInternal方法如下所示。

public class PhoneWindowManager implements WindowManagerPolicy {
	//获取StatusBarManagerInternal对象实例
    StatusBarManagerInternal getStatusBarManagerInternal() {
        synchronized (mServiceAquireLock) {
            if (mStatusBarManagerInternal == null) {
                mStatusBarManagerInternal = LocalServices.getService(StatusBarManagerInternal.class);
            }
            return mStatusBarManagerInternal;
        }
    }
}

getStatusBarManagerInternal就是将存储在LocalServices的StatusBarManagerInternal对象实例取出然后返回。

2、StatusBarManagerInternal是一个抽象接口。

frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java

public interface StatusBarManagerInternal {
	
    void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
            Rect fullscreenBounds, Rect dockedBounds, String cause);

}

3、系统最早是在状态栏管理服务StatusBarManagerService的构造方法中将StatusBarManagerInternal的对象实例存储到LocalServices中的。

frameworks/base/services/core/java/com/android/server/statusbar/StatusBarManagerService.java

public class StatusBarManagerService extends IStatusBarService.Stub {

    public StatusBarManagerService(Context context, WindowManagerService windowManager) {
        mContext = context;
        mWindowManager = windowManager;
		//将mInternalService存储到LocalServices中。
        LocalServices.addService(StatusBarManagerInternal.class, mInternalService);
        LocalServices.addService(GlobalActionsProvider.class, mGlobalActionsProvider);
    }
    
    private final StatusBarManagerInternal mInternalService = new StatusBarManagerInternal() {
        private boolean mNotificationLightOn;
        @Override
        public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
                int mask,
                Rect fullscreenBounds, Rect dockedBounds, String cause) {
            //进一步调用StatusBarManagerService的setSystemUiVisibility方法。
            StatusBarManagerService.this.setSystemUiVisibility(vis, fullscreenStackVis,
                    dockedStackVis, mask, fullscreenBounds, dockedBounds, cause);
        }
     }
     
    private void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
            Rect fullscreenBounds, Rect dockedBounds, String cause) {
        // also allows calls from window manager which is in this process.
        enforceStatusBarService();

        if (SPEW) Slog.d(TAG, "setSystemUiVisibility(0x" + Integer.toHexString(vis) + ")");

        synchronized (mLock) {
        	//进一步调用updateUiVisibilityLocked方法。
            updateUiVisibilityLocked(vis, fullscreenStackVis, dockedStackVis, mask,
                    fullscreenBounds, dockedBounds);
            disableLocked(
                    mCurrentUserId,
                    vis & StatusBarManager.DISABLE_MASK,
                    mSysUiVisToken,
                    cause, 1);
        }
    }
}

由以上代码可知StatusBarManagerInternal的setSystemUiVisibility方法会进一步调用StatusBarManagerService的setSystemUiVisibility方法,StatusBarManagerService的setSystemUiVisibility方法会继续调用updateUiVisibilityLocked。

4、来看下StatusBarManagerService的updateUiVisibilityLocked方法。

public class StatusBarManagerService extends IStatusBarService.Stub {

    private volatile IStatusBar mBar;
    
    //设置监听
    @Override
    public void registerStatusBar(IStatusBar bar, List<String> iconSlots,
            List<StatusBarIcon> iconList, int switches[], List<IBinder> binders,
            Rect fullscreenStackBounds, Rect dockedStackBounds) {
	        enforceStatusBarService();
	        Slog.i(TAG, "registerStatusBar bar=" + bar);
	        mBar = bar;//为mBar赋值
 			...代码省略...
        }
    }
    
    private void updateUiVisibilityLocked(final int vis,
            final int fullscreenStackVis, final int dockedStackVis, final int mask,
            final Rect fullscreenBounds, final Rect dockedBounds) {
        if (mSystemUiVisibility != vis
                || mFullscreenStackSysUiVisibility != fullscreenStackVis
                || mDockedStackSysUiVisibility != dockedStackVis
                || !mFullscreenStackBounds.equals(fullscreenBounds)
                || !mDockedStackBounds.equals(dockedBounds)) {
            mSystemUiVisibility = vis;
            mFullscreenStackSysUiVisibility = fullscreenStackVis;
            mDockedStackSysUiVisibility = dockedStackVis;
            mFullscreenStackBounds.set(fullscreenBounds);
            mDockedStackBounds.set(dockedBounds);
            mHandler.post(new Runnable() {
                    public void run() {
                        if (mBar != null) {//判断mBar是否为空
                            try {
                            	//调用IStatusBar的setSystemUiVisibility方法,触发监听回调
                                mBar.setSystemUiVisibility(vis, fullscreenStackVis, dockedStackVis,
                                        mask, fullscreenBounds, dockedBounds);
                            } catch (RemoteException ex) {
                            }
                        }
                    }
                });
        }
    }

}

updateUiVisibilityLocked方法会调用类型为IStatusBar的属性对象mBar的setSystemUiVisibility方法,mBar对象是在registerStatusBar方法中被赋值的,而且IStatusBar是一个aidl。

frameworks/base/core/java/com/android/internal/statusbar/IStatusBar.aidl

oneway interface IStatusBar
{
	//通知SystemUIVisibility属性发生了变化
    void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis, int mask,
            in Rect fullscreenBounds, in Rect dockedBounds, String packageName);
}

八、系统状态栏StatusBar设置监听回调,获取最新的SystemUIVisibility属性

1、我们在Android 9.0系统源码_SystemUI(二)系统状态栏导航栏的创建和添加这篇文章中有详细介绍StatusBar创建状态栏和导航栏的过程,这里我们只列出了和获取SystemUIVisibility属性相关的代码。

frameworks/base/packages/SystemUI/src/com/android/systemui/SystemBars.java

public class StatusBar extends SystemUI implements DemoMode,
        DragDownHelper.DragDownCallback, ActivityStarter, OnUnlockMethodChangedListener,
        OnHeadsUpChangedListener, CommandQueue.Callbacks, ZenModeController.Callback,
        ColorExtractor.OnColorsChangedListener, ConfigurationListener, NotificationPresenter {
        
    protected CommandQueue mCommandQueue;
    protected IStatusBarService mBarService;

    @Override
    public void start() {
      	...代码省略
        mBarService = IStatusBarService.Stub.asInterface(ServiceManager.getService(Context.STATUS_BAR_SERVICE));
        mCommandQueue = getComponent(CommandQueue.class);
        //mCommandQueue将当前StatusBar 对象添加为事件回调对象
        mCommandQueue.addCallbacks(this);
        //调用StatusBarManagerService的registerStatusBar方法,设置mCommandQueue为setSystemUiVisibility回调对象
        mBarService.registerStatusBar(mCommandQueue, iconSlots, icons, switches, binders,
                    fullscreenStackBounds, dockedStackBounds);
       ...代码省略
   }
   
   //StatusBarManagerService的setSystemUiVisibility方法最终会回调到这里。
   @Override
   public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
       //至此StatusBar才终于成功收到了应用所设置的SystemUiVisibility属性,然后根据具体的属性显示对应的样式
       ..代码省略...
    }
}

2、StatusBar的start方法首先会为类型为IStatusBarService的属性对象mBarService赋值,IStatusBarService是一个aidl。

frameworks/base/core/java/com/android/internal/statusbar/IStatusBarService.aidl

interface IStatusBarService
{
    // You need the STATUS_BAR_SERVICE permission
    void registerStatusBar(IStatusBar callbacks, out List<String> iconSlots,
            out List<StatusBarIcon> iconList,
            out int[] switches, out List<IBinder> binders, out Rect fullscreenStackBounds,
            out Rect dockedStackBounds);
}

通过它我们可以调用到状态栏管理服务StatusBarManagerService的registerStatusBar方法。StatusBar指定了类型为CommandQueue的属性对象mCommandQueue为StatusBarManagerService的setSystemUiVisibility的回调对象,这样StatusBarManagerService对象首先会回调CommandQueue的setSystemUiVisibility方法。

3、CommandQueue和setSystemUiVisibility方法相关的代码如下所示。

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java

public class CommandQueue extends IStatusBar.Stub {

    private static final int MSG_SET_SYSTEMUI_VISIBILITY       = 6 << MSG_SHIFT;
    private ArrayList<Callbacks> mCallbacks = new ArrayList<>();//回调对象集合

	//回调对象
    public interface Callbacks {
        default void setSystemUiVisibility(int vis, int fullscreenStackVis,
                int dockedStackVis, int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
        }
    }
    
    //添加回调对象到mCallbacks集合中
    public void addCallbacks(Callbacks callbacks) {
        mCallbacks.add(callbacks);
        callbacks.disable(mDisable1, mDisable2, false /* animate */);
    }
    
    //StatusBarManagerService对象首先会回调此方法。
    public void setSystemUiVisibility(int vis, int fullscreenStackVis, int dockedStackVis,
            int mask, Rect fullscreenStackBounds, Rect dockedStackBounds) {
        synchronized (mLock) {
            // Don't coalesce these, since it might have one time flags set such as
            // STATUS_BAR_UNHIDE which might get lost.
            SomeArgs args = SomeArgs.obtain();
            args.argi1 = vis;
            args.argi2 = fullscreenStackVis;
            args.argi3 = dockedStackVis;
            args.argi4 = mask;
            args.arg1 = fullscreenStackBounds;
            args.arg2 = dockedStackBounds;
            //发送消息
            mHandler.obtainMessage(MSG_SET_SYSTEMUI_VISIBILITY, args).sendToTarget();
        }
    }
    private final class H extends Handler {
        private H(Looper l) {
            super(l);
        }

        public void handleMessage(Message msg) {
            final int what = msg.what & MSG_MASK;
            switch (what) {
            		...代码省略...
             		case MSG_SET_SYSTEMUI_VISIBILITY:
	                    SomeArgs args = (SomeArgs) msg.obj;
	                    //依次调用集合中对象的setSystemUiVisibility方法,因为StatusBar有调用addCallbacks方法,
	                    //因此这里会触发StatusBar的setSystemUiVisibility方法。
	                    for (int i = 0; i < mCallbacks.size(); i++) {
	                        mCallbacks.get(i).setSystemUiVisibility(args.argi1, args.argi2, args.argi3,
	                                args.argi4, (Rect) args.arg1, (Rect) args.arg2);
	                    }
	                    args.recycle();
                    break; 
                    ...代码省略...
            }
        }
  }
}

CommandQueue的addCallbacks方法会将回调对象(例如前面的StatusBar)添加到mCallbacks集合中,当CommandQueued的setSystemUiVisibility方法被回调的时候,会通过Handler发送消息,并在Handler的handleMessage方法的对应消息分支中依次调用集合中回调对象的setSystemUiVisibility方法,因为StatusBar有调用addCallbacks方法将自己添加到回调集合中,因此这里会触发StatusBar的setSystemUiVisibility方法,至此StatusBar才终于成功收到了应用所设置的SystemUiVisibility属性,然后根据具体的属性显示对应的样式。

你可能感兴趣的:(Framework9源码,android,frameworks)