AndroidR WindowToken

264 阅读6分钟
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
        implements Comparable<WindowContainer>
class RootWindowContainer extends WindowContainer<DisplayContent>
class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>
class WindowState extends WindowContainer<WindowState> 
class WindowToken extends WindowContainer<WindowState>
final class ActivityRecord extends WindowToken
class Task extends WindowContainer<WindowContainer>  
class ActivityStack extends Task

参考:www.kancloud.cn/alex_wsc/an…

final class ActivityRecord extends WindowToken

在ActivityRecord的构造方法中,先调用父类WindowToken的构造函数,得到其WindowToken

因此,ActivityRecord的apptoken(IApplicationToken.Stub)是等价于WindowToken的(强制类型转换)

super(_service.mWindowManager, new Token(_intent).asBinder(), TYPE_APPLICATION, true,
                null /* displayContent */, false /* ownerCanManageAppTokens */);

        mAtmService = _service;
        appToken = (Token) token;
        info = aInfo;
        mUserId = UserHandle.getUserId(info.applicationInfo.uid);
        packageName = info.applicationInfo.packageName;

对于显示组件(客户端)而言的Token,是任意一个Binder的实例,对显示组件(客户端)来说仅仅是一个创建窗口的令牌,没有其他的含义。

Token(Intent intent) {
        name = intent.getComponent().flattenToShortString();
        tokenString = "Token{" + Integer.toHexString(System.identityHashCode(this)) + "}";
        }

WindowToken由token和WindowType来区分,已没有AppWindowToken这个类。添加 WindowToken,也是根据WindowType来添加

class WindowToken extends WindowContainer<WindowState> {

    // The actual token.
    final IBinder token;

    // The type of window this token is for, as per WindowManager.LayoutParams.
    final int windowType;

WindowState是WMS中的窗口实例,其mToken保存了窗口令牌,其ActivityRecord保存了对应的Activity(如果是应用窗口的话,不是应用窗口,则为null)

class WindowState extends WindowContainer<WindowState>
WindowToken mToken;
// The same object as mToken if this is an app window and null for non-app windows.
ActivityRecord mActivityRecord;

WindowToken的构造方法

    WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
            DisplayContent dc, boolean ownerCanManageAppTokens, int ownerUid,
            boolean roundedCornerOverlay, boolean fromClientToken) {
        super(service);
        token = _token;
        windowType = type;
        mPersistOnEmpty = persistOnEmpty;
        mOwnerCanManageAppTokens = ownerCanManageAppTokens;
        mOwnerUid = ownerUid;
        mRoundedCornerOverlay = roundedCornerOverlay;
        mFromClientToken = fromClientToken;
        if (dc != null) {
            dc.addWindowToken(token, this);
        }

最后,是在DisplayContent中添加WindowToken

//DisplayContent.java
void addWindowToken(IBinder binder, WindowToken token) {
            mTokenMap.put(binder, token);
        //如果调用ActivityRecord#asActivityRecord为空,说明其不是Activity的Token
        if (token.asActivityRecord() == null) {

总结:ActivityRecord----->apptoken----->WindowToken----->WindowState

可以由WindowState来处理ActivityRecord的可见性等问题

2020-11-24

ViewManager这个接口,只有三个方法,添加view,updateView布局,删除View

public interface ViewManager {
    void addView(View var1, LayoutParams var2);

    void updateViewLayout(View var1, LayoutParams var2);

    void removeView(View var1);
}

The interface that apps use to talk to the window manager

一个WindowManager实例对应一个Display,获取WindowManager需要Context上下文(Context.getSystemService(Context.WINDOW_SERVICE))

对应其注释

@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {

窗口的布局参数,这个是很重要的

    public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
@WindowType
public int type;
//分为三种,APPLICATION,FIRST_SUB_WINDOW,SYSTEM,在WMS的方法addWindow时,根据其窗口类型的不同,其窗口得到不同WindoToken

这里,简单介绍一下处理的一个故障,与flsgs相关:
      /** Window flag: as long as this window is visible to the user, keep
         *  the device's screen turned on and bright. */
        public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;

值得注意的是,只要Activity对应的WindowState带有这个标志位,即使Activity没有带点亮屏幕的标志位,屏幕也会被对应的WindowState点亮,这是R上新增的亮屏逻辑(PowerManagerservice的wakeUpNoUpdateLocked方法有点亮屏幕的原因)

有一些业务场景需要注意这个:如近感灭屏,第三方通话,Q上不会亮屏,R上会亮屏。Google dialer 52如前所述,最新的dialer已修改这个bug,需要应用做亮屏适配

Activity方面,主要是R上新增的接口containsTurnScreenOnWindow

    boolean getTurnScreenOnFlag() {
        return mTurnScreenOn || containsTurnScreenOnWindow();
    }

    private boolean containsTurnScreenOnWindow() {
        // When we are relaunching, it is possible for us to be unfrozen before our previous
        // windows have been added back. Using the cached value ensures that our previous
        // showWhenLocked preference is honored until relaunching is complete.
        if (isRelaunching()) {
            return mLastContainsTurnScreenOnWindow;
        }
        //可以在这里添加打印log,便比较清楚了
        for (int i = mChildren.size() - 1; i >= 0; i--) {
            if ((mChildren.get(i).mAttrs.flags & LayoutParams.FLAG_TURN_SCREEN_ON) != 0) {
                return true;
            }
        }
        return false;
    }

WindowState方面,主要新增的亮屏判断hasTurnScreenOnFlag

    void prepareWindowToDisplayDuringRelayout(boolean wasVisible) {
        // We need to turn on screen regardless of visibility.
        final boolean hasTurnScreenOnFlag = (mAttrs.flags & FLAG_TURN_SCREEN_ON) != 0
                || (mActivityRecord != null && mActivityRecord.canTurnScreenOn());

这里再介绍一个处理的故障,与窗口令牌有关,主要是子窗口的可见性问题

IBinder,这个是窗口令牌。但system窗口的这个属性没有,即mAttrs.token = null。所以在系统窗口上,添加popupWindow,会导致子窗口没有令牌,从而子窗口显示不出来

        /**
         * Identifier for this window.  This will usually be filled in for
         * you.
         */
        public IBinder token = null;

在WMS#addWindow中

            ActivityRecord activity = null;
            final boolean hasParent = parentWindow != null;
            // Use existing parent window token for child windows since they go in the same token
            // as there parent window so we can apply the same policy on them.
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow.mAttrs.token : attrs.token);
需要做一定的修改:
           Ibinder parentWindow_Token = new Binder();
           if (hasParent){
               parentWindow_Token = parentWindow.mAttrs.token != null ? parentWindow.mAttrs.token : parentWindow.mToken.token;
           }
            WindowToken token = displayContent.getWindowToken(
                    hasParent ? parentWindow_Token : attrs.token);

packageName这个参数不为null,如果为null,会在ViewRootImpl的setView为其赋值的。

        /**
         * Name of the package owning this window.
         */
        public String packageName = null;

    if (mWindowAttributes.packageName == null) {
        mWindowAttributes.packageName = mBasePackageName;
     }

layoutInDisplayCutoutMode 刘海屏控制flag,应用布局,是否占用Notch区域。真正的全面屏,是没有Notch,市面的全面屏,都是根据这个flag,拉伸应用布局,使其占用Notch区域

参考资料:blog.csdn.net/yi_master/a…

        /**
         * Controls how the window is laid out if there is a {@link DisplayCutout}.
         *
         * <p>
         * Defaults to {@link #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT}.
         *
         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT
         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER
         * @see #LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
         * @see DisplayCutout
         * @see android.R.attr#windowLayoutInDisplayCutoutMode
         *         android:windowLayoutInDisplayCutoutMode
         */
        @LayoutInDisplayCutoutMode
        public int layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;

2020-11-25

WindowManagerImpl 成员变量有上下文和父窗口

Provides low-level communication with the system window manager for
operations that are bound to a particular context, display or parent window.
public final class WindowManagerImpl implements WindowManager {
    @UnsupportedAppUsage
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    @VisibleForTesting
    public final Context mContext;
    private final Window mParentWindow;
    private IBinder mDefaultToken;

addView

    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        /**
        // Only use the default token if we don't have a parent window.
        if (mDefaultToken != null && mParentWindow == null) { 
        */
        mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
                mContext.getUserId());
    }

WindowManagerGlobal不带有Context上下文,有WMS和WindowSession的代理

/**
 * Provides low-level communication with the system window manager for
 * operations that are not associated with any particular context.
    @UnsupportedAppUsage
    private static IWindowManager sWindowManagerService;
    @UnsupportedAppUsage
    private static IWindowSession sWindowSession;

保存了添加的所有窗口,RootView及其布局参数

@UnsupportedAppUsage
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams =
        new ArrayList<WindowManager.LayoutParams>();

//Global是单实例对象,一个进程只有一个
   public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {

addView的部分流程分析

            // If this is a panel window, then find the window it is being
            // attached to for future reference.
            //如果其type是子窗口,需要找到其父窗口
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {//窗口令牌相等
                        panelParentView = mViews.get(i);
                    }
                }
            }

            root = new ViewRootImpl(view.getContext(), display);//根据View的上下文新建ViewRootImpl

            view.setLayoutParams(wparams);
            //mView,mRoots,mParams,三个数组一一对应
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);

            // do this last because it fires off messages to start doing things
            try {
                root.setView(view, wparams, panelParentView, userId);

WindowManagerGlobal的大部分功能,都是由ViewRootImpl来完成的。ViewRootImpl是连接客户端与服务端的大管家。

/ * The top of a view hierarchy, implementing the needed protocol between View
 * and the WindowManager.  This is for the most part an internal implementation
 * detail of {@link WindowManagerGlobal}.
*/
public final class ViewRootImpl implements ViewParent,
        View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {

AndroidR上,默认为2,所以,使用 {@link InsetsController}来控制status/navigation bar

    public static int sNewInsetsMode =
            SystemProperties.getInt(USE_NEW_INSETS_PROPERTY, NEW_INSETS_MODE_FULL);
    public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
        synchronized (this) {
            if (mView == null) {
                mView = view;//顶层View,即DecorView,使用mView保存
                //...
                mAdded = true;
                int res; /* = WindowManagerImpl.ADD_OKAY; */

                // Schedule the first layout -before- adding to the window
                // manager, to make sure we do the relayout before receiving
                // any other events from the system.
                requestLayout();//先发出一个请求布局的Handler消息,在添加窗口过后,布局  
                //...
                    //向WMS添加窗口的起点
                    res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mDisplayCutout, inputChannel,
                            mTempInsets, mTempControls);
                    setFrame(mTmpFrame);//WMS初步布局返回的mWinFrame 
                 //...
                view.assignParent(this);//分配View的根节点
                mAddedTouchMode = (res & WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE) != 0;
                mAppVisible = (res & WindowManagerGlobal.ADD_FLAG_APP_VISIBLE) != 0;                

2020-11-29

        @Override
        public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {
            ActivityThread.this.scheduleTransaction(transaction);
        }