1【Android 12】【WCT的定义】WindowContainerTransaction

在这里插入图片描述

一、定义

/**
 * Represents a collection of operations on some WindowContainers that should be applied all at
 * once.
 *
 * @hide
 */
@TestApi
public final class WindowContainerTransaction implements Parcelable {
    ......
}

WindowContainerTransaction表示一些WindowContainer上应该一次性应用的操作集合。

从使用意义上来看,WindowContainerTransaction类和Transaction类比较相似,Transaction是应用在SurfaceControl上的操作集合,WindowContainerTransaction是应用在WindowContainer上的操作集合。另外WindowContainerTransaction也实现了Parcelable,这为其在系统服务端和App端之间的传输提供了支持。

二、使用

结合分屏的一处逻辑,看下WindowContainerTransaction是如何使用的,以下是进入分屏的过程中LegacySplitScreenController#splitPrimaryTask做的事情:

    public boolean splitPrimaryTask() {
        ......
        final WindowContainerTransaction wct = new WindowContainerTransaction();
        // Clear out current windowing mode before reparenting to split task.
        wct.setWindowingMode(topRunningTask.token, WINDOWING_MODE_UNDEFINED);
        wct.reparent(topRunningTask.token, mSplits.mPrimary.token, true /* onTop */);
        mWindowManagerProxy.applySyncTransaction(wct);
        return true;
    }

1)、创建一个WindowContainerTransaction对象。

2)、调用WindowContainerTransaction#setWindowingMode。

3)、调用WindowContainerTransaction#reparent。

4)、调用WindowManagerProxy#applySyncTransaction发送当前WindowContainerTransaction,关于WCT的发送留在以后分析,这里重点分析第2、3点。

1 WindowContainerTransaction#setWindowingMode

    /**
     * Sets the windowing mode of the given container.
     */
    @NonNull
    public WindowContainerTransaction setWindowingMode(
            @NonNull WindowContainerToken container, int windowingMode) {
        Change chg = getOrCreateChange(container.asBinder());
        chg.mWindowingMode = windowingMode;
        return this;
    }

内容很简单:

1)、根据传入的WindowContainerToken对象,通过WindowContainerTransaction#getOrCreateChange方法获取一个Change对象。

2)、将Change的成员变量mWindowingMode赋值为传入的windowingMode。

在分析WindowContainerTransaction#getOrCreateChange方法之前,先看下WindowContainerToken的作用:

/**
 * Interface for a window container to communicate with the window manager. This also acts as a
 * token.
 * @hide
 */
@TestApi
public final class WindowContainerToken implements Parcelable {

    private final IWindowContainerToken mRealToken;

    /** @hide */
    public WindowContainerToken(IWindowContainerToken realToken) {
        mRealToken = realToken;
    }

    private WindowContainerToken(Parcel in) {
        mRealToken = IWindowContainerToken.Stub.asInterface(in.readStrongBinder());
    }

    /** @hide */
    public IBinder asBinder() {
        return mRealToken.asBinder();
    }

	......
}

WindowContainterToken实现了Parcelable,这为其跨进程传输提供了支持。WindowContainterToken内部有一个成员变量mRealToken,是一个IWindowContainerToken类型的token,在系统服务创建WindowContainer时候生成(目前只有DisplayArea和Task),对于WindowContainer来说是一个独特的跨进程的标识。WindowContainerToken可以看做是IBinder类型的token的封装,这个token通过WindowContainerToken#asBinder返回。

Task可以通过Task#fillTaskInfo方法将该Task对应的WindowContainerToken保存在对应的TaskInfo中,这样App进程可以先获取TaskInfo进而拿到这个token。

App端如果想通过WindowContainerTransaction的方法修改某个WindowContainer的属性,必须传入该WindowContainer对应的token,这样系统服务端在接收到这个WindowContainerTransaction的时候,才可以通过token知道需要修改哪些WindowContainer。

接着看下WindowContainerTransaction#getOrCreateChange的内容:

    private Change getOrCreateChange(IBinder token) {
        Change out = mChanges.get(token);
        if (out == null) {
            out = new Change();
            mChanges.put(token, out);
        }
        return out;
    }

从mChanges中查找该WindowContainerToken有没有相应的Change对象,没有就创建一个,然后以键值对的方式把这个WindowContainerToken对象和为其创建的Change对象加入到mChanges中。

mChanges是一个ArrayMap类型的WindowContainerTransaction的成员变量,以IBinder类型的WindowContainerToken对象为key,以WindowContainerTransaction为该WindowContainerToken创建的Change对象为value:

private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();

后续该WindowContainerTransaction发送到WM端的时候,WindowOrganizerController遍历这个WindowContainerTransaction的mChanges成员变量,对于mChanges中的每一个Change对象:

1)、从该Change中提取出WindowContainerToken,将该WindowContainerToken转化为WM端对应的WindowContainer对象。

2)、提取出Change中为该WindowContainerToken保存的WindowingMode,然后应用到第1步中转化得到的WindowContainer。

上面的第2步可以看WindowOrganizerController#applyChanges方法进一步了解一下:

    private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {
        .......
        final int windowingMode = change.getWindowingMode();
        ......
        if (windowingMode > -1) {
            ......
            container.setWindowingMode(windowingMode);
        }
        return effects;
    }

看到这里似乎对WindowContainerTransaction的运作方式有了一部分的了解了,App端如果想要通过WindowContainerTransaction修改WM端的WindowContainter的属性,需要这几步操作:

1)、App端首先要能够拿到这个WindowContainer对应的WindowContainerToken。

2)、创建一个WindowContainerTransaction对象,调用WindowContainerTransaction的相关方法对这个WindowContainerToken进行设置。

3)、发送WindowContainerTransaction。

2 WindowContainerTransaction#setFocusable

为了印证这个猜想,再看另外一个类似的方法WindowContainerTransaction#setFocusable。调用的地方在分屏相关逻辑处:

    public void setHomeMinimized(final boolean minimized) {
		......
        WindowContainerTransaction wct = new WindowContainerTransaction();
        final boolean minimizedChanged = mMinimized != minimized;
        // Update minimized state
        if (minimizedChanged) {
            mMinimized = minimized;
        }
        // Always set this because we could be entering split when mMinimized is already true
        wct.setFocusable(mSplits.mPrimary.token, !mMinimized);

		......
    }

先创建一个WindowContainerTransaction,然后调用WindowContainerTransaction.setFocusable方法设置:

    /**
     * Sets whether a container or any of its children can be focusable. When {@code false}, no
     * child can be focused; however, when {@code true}, it is still possible for children to be
     * non-focusable due to WM policy.
     */
    @NonNull
    public WindowContainerTransaction setFocusable(
            @NonNull WindowContainerToken container, boolean focusable) {
        Change chg = getOrCreateChange(container.asBinder());
        chg.mFocusable = focusable;
        chg.mChangeMask |= Change.CHANGE_FOCUSABLE;
        return this;
    }

该方法用来设置一个WindowContainer和其子WindowContainer是否可以获取焦点,同样是两步操作:

1)、根据传入的WindowContainerToken对象,通过WindowContainerTransaction#getOrCreateChange方法获取一个Change对象。

2)、将Change的成员变量mFocusable赋值为传入的focusable参数。

Change.mChange应用的地方依然是WindowOrganizerController#applyChanges:

    private int applyChanges(WindowContainer container, WindowContainerTransaction.Change change) {
        ......
        if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
            if (container.setFocusable(change.getFocusable())) {
                effects |= TRANSACT_EFFECTS_LIFECYCLE;
            }
        }
        ......
    }

套路都是一样的:

1)、从该Change中提取出WindowContainerToken,将该WindowContainerToken转化为WM端对应的WindowContainer对象。

2)、提取出Change中为该WindowContainerToken保存的focusable属性,然后应用到第1步中转化得到的WindowContainer。

3 WindowContainerTransaction.Change类

    /**
     * Holds changes on a single WindowContainer including Configuration changes.
     * @hide
     */
    public static class Change implements Parcelable {
        public static final int CHANGE_FOCUSABLE = 1;
        public static final int CHANGE_BOUNDS_TRANSACTION = 1 << 1;
        public static final int CHANGE_PIP_CALLBACK = 1 << 2;
        public static final int CHANGE_HIDDEN = 1 << 3;
        public static final int CHANGE_BOUNDS_TRANSACTION_RECT = 1 << 4;
        public static final int CHANGE_IGNORE_ORIENTATION_REQUEST = 1 << 5;
        private final Configuration mConfiguration = new Configuration();
        private boolean mFocusable = true;
        private boolean mHidden = false;
        private boolean mIgnoreOrientationRequest = false;
        private int mChangeMask = 0;
        private @ActivityInfo.Config int mConfigSetMask = 0;
        private @WindowConfiguration.WindowConfig int mWindowSetMask = 0;
        private Rect mPinnedBounds = null;
        private SurfaceControl.Transaction mBoundsChangeTransaction = null;
        private Rect mBoundsChangeSurfaceBounds = null;
        private int mActivityWindowingMode = -1;
        private int mWindowingMode = -1;
        ......
        public int getWindowingMode() {
            return mWindowingMode;
        }
        public int getActivityWindowingMode() {
            return mActivityWindowingMode;
        }
        public Configuration getConfiguration() {
            return mConfiguration;
        }
        /** Gets the requested focusable state */
        public boolean getFocusable() {
            if ((mChangeMask & CHANGE_FOCUSABLE) == 0) {
                throw new RuntimeException("Focusable not set. check CHANGE_FOCUSABLE first");
            }
            return mFocusable;
        }
        /** Gets the requested hidden state */
        public boolean getHidden() {
            if ((mChangeMask & CHANGE_HIDDEN) == 0) {
                throw new RuntimeException("Hidden not set. check CHANGE_HIDDEN first");
            }
            return mHidden;
        }
        /** Gets the requested state of whether to ignore orientation request. */
        public boolean getIgnoreOrientationRequest() {
            if ((mChangeMask & CHANGE_IGNORE_ORIENTATION_REQUEST) == 0) {
                throw new RuntimeException("IgnoreOrientationRequest not set. "
                        + "Check CHANGE_IGNORE_ORIENTATION_REQUEST first");
            }
            return mIgnoreOrientationRequest;
        }
        public int getChangeMask() {
            return mChangeMask;
        }
        @ActivityInfo.Config
        public int getConfigSetMask() {
            return mConfigSetMask;
        }
        @WindowConfiguration.WindowConfig
        public int getWindowSetMask() {
            return mWindowSetMask;
        }
        /**
         * Returns the bounds to be used for scheduling the enter pip callback
         * or null if no callback is to be scheduled.
         */
        public Rect getEnterPipBounds() {
            return mPinnedBounds;
        }
        public SurfaceControl.Transaction getBoundsChangeTransaction() {
            return mBoundsChangeTransaction;
        }
        public Rect getBoundsChangeSurfaceBounds() {
            return mBoundsChangeSurfaceBounds; 
        }
        ......
    }

持有对单一WindowContainer的包括Configuration的修改。

通过对WindowContainerTransaction#setWindowingMode和WindowContainerTransaction#setFocusable这两部分的分析,可以看到不管是改变WindowContainer的windowingMode,或是focusable属性,都需要:

1)、创建一个WindowContainerTransaction对象,调用WindowContainerTransaction提供的方法对WindowContainerToken进行设置,实际上就是将客户端对WindowContainer期望的一些修改先保存到WindowContainerTransaction为WindowContainerToken创建的Change对象的相关成员变量中。

2)、发送创建的WindowContainerTransaction到WM端,WM端获取到WindowContainerTransaction.Change相关成员变量的值,然后应用到WindowContainer上。

那么也就是说,WindowContainerTransaction.Change有多少成员变量,WindowContainerTransaction就可以向客户端提供多少可以改变WindowContainer的方法接口。

看下WindowContainerTransaction.Change的成员变量都有哪些:

        private final Configuration mConfiguration = new Configuration();
        private boolean mFocusable = true;
        private boolean mHidden = false;
        private boolean mIgnoreOrientationRequest = false;
        ......
        private Rect mPinnedBounds = null;
        private SurfaceControl.Transaction mBoundsChangeTransaction = null;
        private Rect mBoundsChangeSurfaceBounds = null;
        private int mActivityWindowingMode = -1;
        private int mWindowingMode = -1;

重点看一下成员变量mConfiguration,毕竟Configuration中包含了很多的配置属性,但是WindowContainerTransaction并不支持对Configuration中所有的属性进行修改,主要是screenSize、windowingMode和bounds等。

目前来看设计WindowContainerTransaction是为多窗口功能服务的,因此WindowContainerTransaction.Change提供的这些方法已经满足需要了。之前都是SystemUI直接跨Binder调用系统服务的一些resizeStack之类的方法去修改分屏Task的bounds。有了WindowContainerTransaction.Chang之后,就可以把系统服务向客户端提供的对WindowContainer的所有修改方法统一组织起来,方便管理以及后续扩展新内容。

4 WindowContainerTransaction#reparent

我们上面只分析了LegacySplitScreenController#splitPrimaryTask中的一个方法WindowContainerTransaction#setWindowingMode,还有一个方法WindowContainerTransaction#reparent没有分析。

    /**
     * Reparents a container into another one. The effect of a {@code null} parent can vary. For
     * example, reparenting a stack to {@code null} will reparent it to its display.
     *
     * @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
     *              the bottom.
     */
    @NonNull
    public WindowContainerTransaction reparent(@NonNull WindowContainerToken child,
            @Nullable WindowContainerToken parent, boolean onTop) {
        mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),
                parent == null ? null : parent.asBinder(),
                onTop));
        return this;
    }

该方法的作用是将一个WindowContainer重新reparent到另外一个WindowContainer,最终结果受到参数parent的影响,如果parent为null,那么将参数child子WindowContainer容器reparent到它对应的display上。

看下createForReparent方法:

        public static HierarchyOp createForReparent(
                @NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
            return new HierarchyOp(HIERARCHY_OP_TYPE_REPARENT,
                    container, reparent, null, null, toTop, null);
        }

再看下mHierarchyOps:

    // Flat list because re-order operations are order-dependent
    private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();

因此这个方法的内容是:

1)、根据传入的IBinder类型container和reparent创建一个HierarchyOp对象。

2)、将这个HierarchyOp对象添加到WindowContainerTransaction的成员变量mHierarchyOps中。

后续这个WindowContainerTransaction发送到系统服务端的时候,由WindowOrganizerController负责将以上两个IBinder类型的token转化为WindowContainer对象,然后进行reparent操作。

对比一下WindowContainerTransaction.Change的相关内容:

1)、WindowContainerTransaction创建的Change对象是和WindowContainerToken内部的token对象一一对应的,创建的时候会判断WindowContainerTransaction的mChanges中是否已经有了一个与token对应的Change对象,对应的是

2)、HierarchyOp对象在每次需要进行reparent的时候就会创建一次,对应的是一次层次结构上的操作。

5 WindowContainerTransaction.HierarchyOp类

在做出进一步的总结前,还是需要看下WindowContainerTransaction.HierarchyOp类的作用。

        private HierarchyOp(int type, @Nullable IBinder container, @Nullable IBinder reparent,
                int[] windowingModes, int[] activityTypes, boolean toTop,
                @Nullable Bundle launchOptions) {
            mType = type;
            mContainer = container;
            mReparent = reparent;
            mWindowingModes = windowingModes != null ?
                    Arrays.copyOf(windowingModes, windowingModes.length) : null;
            mActivityTypes = activityTypes != null ?
                    Arrays.copyOf(activityTypes, activityTypes.length) : null;
            mToTop = toTop;
            mLaunchOptions = launchOptions;
        }

创建HierarchyOp对象的时候会对其成员变量进行赋值,比较重要的是其中三个成员变量:

        // Container we are performing the operation on.
        private final IBinder mContainer;
        // If this is same as mContainer, then only change position, don't reparent.
        private final IBinder mReparent;
        // Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
        private final boolean mToTop;

mContainer是子WindowContainer对应的token,mReparent是父WindowContainer对应的token,mToTop表示reparent操作后是否需要将子WindowContainer移动到父WindowContainer的top。

这样来看,WindowContainerTransaction.HierarchyOp的作用逻辑和WindowContainerTransaction.Change类似。WindowContainerTransaction.HierarchyOp将App端设置的一次WindowContainer层级调整操作中的子WindowContainer对应的token和父WindowContainer对应的token保存起来,后续WindowContainerTransaction发送到服务端后,服务端读取HierarchyOp中token并转化为对应的WindowContainer对象,再完成此次WindowContainer层级调整。

根据mReparent的值,会有几种不同的情况:

1)、mReparent不为空,且不等于mContainer,这个是最普遍的reparent的操作,将子WindowContainer移入父WindowContainer中。

2)、mReparent不为空,且等于mContainer,那么此次操作不是reparent,而是reorder,根据mToTop的值调整mContainer在当前父容器中的位置。

3)、mReparent为空,那么将mContainer移入当前display中。

具体的代码情况在WCT的应用一文中详细分析。

三、总结

WindowContainerTransaction向客户端提供了远程修改系统服务中的WindowContainer的能力,类似于Transaction修改SurfaceControl,直白点说就是,由于客户端无法直接修改WindowContainer,所以客户端需要先告诉WindowContainerTransaction我想修改哪些WindowContainer的哪些内容,WindowContainerTransaction把这些期望的修改保存起来,后续客户端把这个WindowContainerTransaction发送到服务端,服务端读取WindowContainerTransaction中的设置后由服务端帮助客户端完成修改。

一般流程是:

1)、客户端创建一个WindowContainerTransaction对象。

2)、调用WindowContainerTransaction的相关方法,这一步需要将期望修改的WindowContainer对应的WindowContaienrToken对象作为参数传入。

3)、通过WindowOrganizer将WindowContainerTransaction发送到服务端,最终服务端读取WindowContainerTransaction中保存的参数完成相应操作。

WindowContainerTransaction支持以下两类修改:

1)、修改WindowContainer的属性,包括WindowingMode和Configuration之类,这类修改保存在WindowContainerTransaction.Change类中。

2)、修改WindowContainer的层级,既可以将一个子容器从当前父容器移入另外一个新的父容器中,也可以仅仅调整子容器在当前父容器中的位置,这类修改保存在WindowContainerTransaction.HierarchyOp类中。

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值