WMS添加窗口流程和设置窗口Z轴

222 阅读8分钟

窗口类型

窗口类型定义在 WindowManager.LayoutParams 中。可以分为三种:应用窗口、子窗口、系统窗口。取值范围为:

  • 应用窗口: 1~999
  • 子窗口: 1000~1999
  • 系统窗口: 2000~2999

class LayoutParams {
    // 应用窗口的取值范围在 1~999
    // 一般的应用窗口
    public static final int FIRST_APPLICATION_WINDOW = 1;

    // 应用窗口起始值
    public static final int TYPE_BASE_APPLICATION   = 1;

    // 应用程序窗口,token 必须是 ActivityRecord
    public static final int TYPE_APPLICATION        = 2;

    // 应用程序启动窗口
    public static final int TYPE_APPLICATION_STARTING = 3;

    public static final int TYPE_DRAWN_APPLICATION = 4;
    // 应用窗口结束值
    public static final int LAST_APPLICATION_WINDOW = 99;


    // 子窗口的取值范围在 1000~1999
    // 应用子窗口起始值
    public static final int FIRST_SUB_WINDOW = 1000;

    public static final int TYPE_APPLICATION_PANEL = FIRST_SUB_WINDOW;

    public static final int TYPE_APPLICATION_MEDIA = FIRST_SUB_WINDOW + 1;

        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW + 2;

    public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW + 3;

    public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW + 4;

    public static final int TYPE_APPLICATION_ABOVE_SUB_PANEL = FIRST_SUB_WINDOW + 5;

    // 子窗口结束值
    public static final int LAST_SUB_WINDOW = 1999;


    // 系统窗口的取值范围在 2000~2999
    // 系统窗口起始值
    public static final int FIRST_SYSTEM_WINDOW     = 2000;

    // 状态栏
    public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;

    // 搜索栏
    public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;

    // 拨号窗口
    public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;

    // 提醒窗口
    public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;

    // 锁屏窗口
    public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;

    // Toast窗口
    public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;

    // OVERLAY窗口,不能获取焦点
    public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;

    // 拨号窗口,可以在锁屏窗口之上,不能获取焦点
    public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;

    // 从状态栏划出的对话框
    public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;

    // 锁屏窗口
    public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;

    // 系统错误提示窗口
    public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;

    // 输入法窗口
    public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;

    // 输入法对话框,在当前的输入法窗口之上
    public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;

    // 壁纸窗口
    public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;

    // 从状态栏划出的面板窗口
    public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;

    // 系统安全相关的窗口
    public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;

    // drag-and-drop 假窗口
    public static final int TYPE_DRAG               = FIRST_SYSTEM_WINDOW+16;

    // 从状态栏划出的面板窗口
    public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17;

    // 显示鼠标的窗口
    public static final int TYPE_POINTER = FIRST_SYSTEM_WINDOW+18;

    // 导航栏
    public static final int TYPE_NAVIGATION_BAR = FIRST_SYSTEM_WINDOW+19;

    // 音量窗口
    public static final int TYPE_VOLUME_OVERLAY = FIRST_SYSTEM_WINDOW+20;

    // 启动进度窗口
    public static final int TYPE_BOOT_PROGRESS = FIRST_SYSTEM_WINDOW+21;

    // systemUI 隐藏时消费输入事件
    public static final int TYPE_INPUT_CONSUMER = FIRST_SYSTEM_WINDOW+22;

    // 导航栏面板
    public static final int TYPE_NAVIGATION_BAR_PANEL = FIRST_SYSTEM_WINDOW+24;

    // 屏幕 overlay 窗口
    public static final int TYPE_DISPLAY_OVERLAY = FIRST_SYSTEM_WINDOW+26;

    // 无障碍服务时的放大显示的窗口
    public static final int TYPE_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW+27;

    // 虚拟屏的演示窗口
    public static final int TYPE_PRIVATE_PRESENTATION = FIRST_SYSTEM_WINDOW+30;

    // 语音交换的窗口
    public static final int TYPE_VOICE_INTERACTION = FIRST_SYSTEM_WINDOW+31;

    // 无障碍服务时的 overlay 窗口
    public static final int TYPE_ACCESSIBILITY_OVERLAY = FIRST_SYSTEM_WINDOW+32;

    // 语音交换的启动窗口
    public static final int TYPE_VOICE_INTERACTION_STARTING = FIRST_SYSTEM_WINDOW+33;

    public static final int TYPE_DOCK_DIVIDER = FIRST_SYSTEM_WINDOW+34;

    // 快速设置磁铁的窗口
    public static final int TYPE_QS_DIALOG = FIRST_SYSTEM_WINDOW+35;

    // 截屏窗口
    public static final int TYPE_SCREENSHOT = FIRST_SYSTEM_WINDOW + 36;

    // 演示窗口
    public static final int TYPE_PRESENTATION = FIRST_SYSTEM_WINDOW + 37;

    // 应用 overlay 窗口,在应用窗口和系统重要窗口之间
    public static final int TYPE_APPLICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 38;

    // 无障碍服务的 overlay 
    public static final int TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY = FIRST_SYSTEM_WINDOW + 39;

    // 锁屏通知
    public static final int TYPE_NOTIFICATION_SHADE = FIRST_SYSTEM_WINDOW + 40;

    // 状态栏附加层
    public static final int TYPE_STATUS_BAR_ADDITIONAL = FIRST_SYSTEM_WINDOW + 41;

    // 系统窗口结束值
    public static final int LAST_SYSTEM_WINDOW      = 2999;
}

窗口Z轴

窗口的Z轴,由 mBaseLayer 和 mSubLayer 描述,在 WindowState 初始化设置,并且不可改变。

WindowManagerPolicy 根据 windowType 进行转换后,加上乘数(10000)和偏移量(1000)。容器中可能有多个相同类型的窗口,这样可以保证有足够的空间。

WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
        WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
        int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
        PowerManagerWrapper powerManagerWrapper) {
    ...
    // 子窗口类型
    if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
        // 使用父窗口的 window type 进行转换
        mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
                * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
        // 相对父窗口的偏移量
        mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
        mIsChildWindow = true;
        ...
    // 应用主窗口、系统窗口类型
    } else {
        mBaseLayer = mPolicy.getWindowLayerLw(this)
                * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
        mSubLayer = 0;
        ...
    }
    ...
}

int TYPE_LAYER_MULTIPLIER = 10000;
int TYPE_LAYER_OFFSET = 1000;

WindowManagerPolicy.java

getWindowLayerFromTypeLw 获取应用窗口和系统窗口的 layer,根据 window type 进行转换。

取值范围: 1~36

应用窗口是 2;壁纸是 1,要求在下层;其它在 3~36;

getSubWindowLayerFromTypeLw 获取子窗口相对于应用窗口的相对位置。

取值范围: -2~3.

负数表示在应用窗口的下方,正数表示在应用窗口的上方。

default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow,
        boolean roundedCornerOverlay) {
    if (roundedCornerOverlay && canAddInternalSystemWindow) {
        return getMaxWindowLayer();  // 36
    }
    if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
        return APPLICATION_LAYER;  // 2
    }

    switch (type) {
        case TYPE_WALLPAPER:
            // 壁纸在最下面
            return  1;
        case TYPE_PRESENTATION:
        case TYPE_PRIVATE_PRESENTATION:
        case TYPE_DOCK_DIVIDER:
        case TYPE_QS_DIALOG:
        case TYPE_PHONE:
            return  3;
        case TYPE_SEARCH_BAR:
            return  4;
        case TYPE_INPUT_CONSUMER:
            return  5;
        case TYPE_SYSTEM_DIALOG:
            return  6;
        case TYPE_TOAST:
            return  7;
        case TYPE_PRIORITY_PHONE:
            return  8;
        case TYPE_SYSTEM_ALERT:
            // ANR / app crashed dialogs
            return  canAddInternalSystemWindow ? 12 : 9; // 是否系统APP
        case TYPE_APPLICATION_OVERLAY:
            return  11;
        case TYPE_INPUT_METHOD:
            return  13;
        case TYPE_INPUT_METHOD_DIALOG:
            return  14;
        case TYPE_STATUS_BAR:
            return  15;
        case TYPE_STATUS_BAR_ADDITIONAL:
            return  16;
        case TYPE_NOTIFICATION_SHADE:
            return  17;
        case TYPE_STATUS_BAR_SUB_PANEL:
            return  18;
        case TYPE_KEYGUARD_DIALOG:
            return  19;
        case TYPE_VOICE_INTERACTION_STARTING:
            return  20;
        case TYPE_VOICE_INTERACTION:
            return  21;
        case TYPE_VOLUME_OVERLAY:
            return  22;
        case TYPE_SYSTEM_OVERLAY:
            return  canAddInternalSystemWindow ? 23 : 10;
        case TYPE_NAVIGATION_BAR:
            return  24;
        case TYPE_NAVIGATION_BAR_PANEL:
            return  25;
        case TYPE_SCREENSHOT:
            return  26;
        case TYPE_SYSTEM_ERROR:
            return  canAddInternalSystemWindow ? 27 : 9;
        case TYPE_MAGNIFICATION_OVERLAY:
            return  28;
        case TYPE_DISPLAY_OVERLAY:
            return  29;
        case TYPE_DRAG:
            return  30;
        case TYPE_ACCESSIBILITY_OVERLAY:
            return  31;
        case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
            return 32;
        case TYPE_SECURE_SYSTEM_OVERLAY:
            return  33;
        case TYPE_BOOT_PROGRESS:
            return  34;
        case TYPE_POINTER:
            return  35;
        default:
            return 3;
    }
}

default int getSubWindowLayerFromTypeLw(int type) {
    switch (type) {
        case TYPE_APPLICATION_PANEL:
        case TYPE_APPLICATION_ATTACHED_DIALOG:
            return APPLICATION_PANEL_SUBLAYER; // -1
        case TYPE_APPLICATION_MEDIA:
            return APPLICATION_MEDIA_SUBLAYER; // -2
        case TYPE_APPLICATION_MEDIA_OVERLAY:
            return APPLICATION_MEDIA_OVERLAY_SUBLAYER; // -1
        case TYPE_APPLICATION_SUB_PANEL:
            return APPLICATION_SUB_PANEL_SUBLAYER;     // 2
        case TYPE_APPLICATION_ABOVE_SUB_PANEL:
            return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER; // 3
    }
    Slog.e("WindowManager", "Unknown sub-window type: " + type);
    return 0;
}

    int APPLICATION_LAYER = 2;
    int APPLICATION_MEDIA_SUBLAYER = -2;
    int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1;
    int APPLICATION_PANEL_SUBLAYER = 1;
    int APPLICATION_SUB_PANEL_SUBLAYER = 2;
    int APPLICATION_ABOVE_SUB_PANEL_SUBLAYER = 3;

addWindow()

  1. 权限检查、错误检查:屏幕不存在、重复添加Window、布局参数检查(主要基于 window tyep)、WindowToken 与 WindowState 的窗口类型一致(非子窗口)等;
  2. 根据传入的token(IBinder)查找或创建 WindowToken; 应用使用 ActivityRecord, 子窗口类型使用父窗口的 token;WindowToken 构造方法中加入层级结构和mTokenMap;
  3. 创建 WindowState, WIndowState 初始化的时候会确定Z轴相关的layer;
  4. 调用openInputChannel
  5. 如果是Toast类型,发送延时隐藏消息;
  6. WindowState 加入 mWindowMap;
  7. 把 WindowState 加入 WindowToken 的子容器(按照Z轴排序);非子窗口类型,子窗口是其父窗口的子容器。过程中,创建 WindowToken 的 SurfaceControl, 调用 WindowToken 的父容器重新分配Z轴。
  8. 设置之后的窗口动画的类型为 ENTER;
  9. 处理可能带来的壁纸、焦点、Ime Target 变化;
  10. 重新设置Z轴
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
        int displayId, int requestUserId, @InsetsType int requestedVisibleTypes,
        InputChannel outInputChannel, InsetsState outInsetsState,
        InsetsSourceControl.Array outActiveControls, Rect outAttachedFrame,
        float[] outSizeCompatScale) {
    int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
            appOp);

    WindowState parentWindow = null;
    final int type = attrs.type;

    synchronized (mGlobalLock) {
        ...
        final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);

        // 权限检查、错误检查:屏幕不存在、重复添加Window、布局参数检查(主要基于 window tyep)、
        // WindowToken 与 WindowState 的窗口类型一致(非子窗口)等
        // 省略
        if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
            parentWindow = windowForClientLocked(null, attrs.token, false);
            ...
        }

        ActivityRecord activity = null;
        final boolean hasParent = parentWindow != null;
        // 子窗口使用父窗口的 token ,可以应用相同的策略
        // 根据传入的token(IBinder)查找 WindowToken
        WindowToken token = displayContent.getWindowToken(
                hasParent ? parentWindow.mAttrs.token : attrs.token);
        // 如果是子窗口,检查其父窗口的type
        final int rootType = hasParent ? parentWindow.mAttrs.type : type;

        final IBinder windowContextToken = attrs.mWindowContextToken;

        if (token == null) {
            if (hasParent) {
                // 子窗口使用父窗口的 token
                token = parentWindow.mToken;
            } else if (mWindowContextListenerController.hasListener(windowContextToken)) {
                final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;
                final Bundle options = mWindowContextListenerController
                        .getOptions(windowContextToken);             
                token = new WindowToken.Builder(this, binder, type)
                        .setDisplayContent(displayContent)
                        .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                        .setRoundedCornerOverlay(isRoundedCornerOverlay)
                        .setFromClientToken(true)
                        .setOptions(options)
                        .build();
            } else {
                // 创建 WIndowToken, 构造方法中加入层级结构和mTokenMap;
                final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
                token = new WindowToken.Builder(this, binder, type)
                        .setDisplayContent(displayContent)
                        .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                        .setRoundedCornerOverlay(isRoundedCornerOverlay)
                        .build();
            }
        } else if (rootType >= FIRST_APPLICATION_WINDOW
                && rootType <= LAST_APPLICATION_WINDOW) {
            activity = token.asActivityRecord();
            ...

        } else if (token.asActivityRecord() != null) {
            // It is not valid to use an app token with other system types; we will
            // instead make a new token for it (as if null had been passed in for the token).
            attrs.token = null;
            token = new WindowToken.Builder(this, client.asBinder(), type)
                    .setDisplayContent(displayContent)
                    .setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow)
                    .build();
        }

        final WindowState win = new WindowState(this, session, client, token, parentWindow,
                appOp[0], attrs, viewVisibility, session.mUid, userId,
                session.mCanAddInternalSystemWindow);

        final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();
        displayPolicy.adjustWindowParamsLw(win, win.mAttrs);
        win.setRequestedVisibleTypes(requestedVisibleTypes);

        final boolean openInputChannels = (outInputChannel != null
                && (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
        if  (openInputChannels) {
            win.openInputChannel(outInputChannel);
        }

        // 如果是Toast类型,发送延时隐藏消息
        if (type == TYPE_TOAST) {
            mAtmService.setToastWindow();
            if (addToastWindowRequiresToken
                    || (attrs.flags & FLAG_NOT_FOCUSABLE) == 0
                    || displayContent.mCurrentFocus == null
                    || displayContent.mCurrentFocus.mOwnerUid != callingUid) {
                mH.sendMessageDelayed(
                        mH.obtainMessage(H.WINDOW_HIDE_TIMEOUT, win),
                        win.mAttrs.hideTimeoutMilliseconds);
            }
        }

        // From now on, no exceptions or errors allowed!
        res = ADD_OKAY;

        win.attach(); // 创建 SurfaceSession
        
        // WindowState 加入 mWindowMap
        mWindowMap.put(client.asBinder(), win);
        
        // 处理可能带来的壁纸、焦点、Ime Target 变化,省略
        boolean imMayMove = true;
        // 把 WindowState 加入 WindowToken 的子容器(按照Z轴排序);非子窗口类型,子窗口是其父窗口的子容器。
        // 过程中,创建 WindowToken 的 SurfaceControl, 调用 WindowToken 的父容器重新分配Z轴。
        win.mToken.addWindow(win);  // WindowToken 
        displayPolicy.addWindowLw(win, attrs);

        // 之后的窗口动画类型是 ENTER 而不是 SHOW
        final WindowStateAnimator winAnimator = win.mWinAnimator;
        winAnimator.mEnterAnimationPending = true;
        winAnimator.mEnteringAnimation = true;

        // window 显示的时候,调用 relayout 计算位置大小
        win.getParent().assignChildLayers();
        ...
    }
    ...
    return res;
}