WCT系列(三):WindowOrganizerController

WCT系列(一):WindowContainerTransaction类详解
WCT系列(二):SyncTransactionQueue类详解
WCT系列(三):WindowOrganizerController
WCT系列(四):BLASTSyncEngine

1、 applySyncTransaction:

在上一篇代码分析中,知道了在应用侧创建的WindowContainerTransaction会通过WindowOrganizer中的applySyncTransaction方法发送到系统侧,然后再系统侧维护的窗口管理库中应用这些修改。
所以本次继续前一篇博客的脚步,看到WindowOrganizerController中的applySyncTransaction方法:

@Override
public int applySyncTransaction(WindowContainerTransaction t,
        IWindowContainerTransactionCallback callback) {
    if (t == null) {
        throw new IllegalArgumentException("Null transaction passed to applySyncTransaction");
    }
    enforceTaskPermission("applySyncTransaction()");
    final CallerInfo caller = new CallerInfo(); //(1)
    final long ident = Binder.clearCallingIdentity();
    try {
        synchronized (mGlobalLock) {
            if (callback == null) {  //(2)
                applyTransaction(t, -1 /* syncId*/, null /*transition*/, caller);
                return -1;
            }

            /**
             * If callback is non-null we are looking to synchronize this transaction by
             * collecting all the results in to a SurfaceFlinger transaction and then delivering
             * that to the given transaction ready callback. See {@link BLASTSyncEngine} for the
             * details of the operation. But at a high level we create a sync operation with a
             * given ID and an associated callback. Then we notify each WindowContainer in this
             * WindowContainer transaction that it is participating in a sync operation with
             * that ID. Once everything is notified we tell the BLASTSyncEngine "setSyncReady"
             * which means that we have added everything to the set. At any point after this,
             * all the WindowContainers will eventually finish applying their changes and notify
             * the BLASTSyncEngine which will deliver the Transaction to the callback.
             */
            final BLASTSyncEngine.SyncGroup syncGroup = prepareSyncWithOrganizer(callback);   //(3)
            final int syncId = syncGroup.mSyncId;
            if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) { //(4)
                mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup); 
                applyTransaction(t, syncId, null /*transition*/, caller);
                setSyncReady(syncId);
            } else {
                // Because the BLAST engine only supports one sync at a time, queue the
                // transaction.
                mService.mWindowManager.mSyncEngine.queueSyncSet(
                        () -> mService.mWindowManager.mSyncEngine.startSyncSet(syncGroup),
                        () -> {
                            applyTransaction(t, syncId, null /*transition*/, caller);
                            setSyncReady(syncId);
                        });
            }
            return syncId;
        }
    } finally {
        Binder.restoreCallingIdentity(ident);
    }
}

CallerInfo() {
    mPid = Binder.getCallingPid();
    mUid = Binder.getCallingUid();
}

private BLASTSyncEngine.SyncGroup prepareSyncWithOrganizer(
        IWindowContainerTransactionCallback callback) { //(5)
    final BLASTSyncEngine.SyncGroup s = mService.mWindowManager.mSyncEngine
            .prepareSyncSet(this, "", BLASTSyncEngine.METHOD_BLAST);
    mTransactionCallbacksByPendingSyncId.put(s.mSyncId, callback);
    return s;
}

方法有点长,但是其实一大半都是注释,首先还记的在WindowOrganizer中调过来时,这个参数列表中的Callback和t分别是什么吗? 答案是SyncCallback中的mWCT和SyncCallback的this。
代码(1)处,会创建一个CallerInfo的对象caller,因为当前的方法就是通过binder调用到的,所以这里就是通过Binder去获取调用端的Pid和Uid,从而获取caller端的信息。
(2)处的条件显然不满足,所以这里就直接跳过,来到(3)处,这里会创建SyncSet,具体实现在(5)处。查看prepareSyncWithOrganizer方法,这里有调用一个prepareSyncSet方法,方法的功能会在另外的模块说,这里先了解下SyncGroup的大概含义和功能就行。SyncGroup是BLASTSyncEngine类中的一个内部类,SyncGroup有一个成员变量mRootMembers,类型为ArraySet,在一次动画过程中,如果某个WindowContainer参与其中,就需要将其放入这个mRootMembers之中。其构造函数也如(7)所示,这里要着重关注的就是SyncGroup对象的创建只在(6)这一个地方,而SyncGroup的id,则会随着每次创建SyncGroup的对象,通过mNextSyncId进行递增。所以每个SyncGroup的id都是唯一的。 剩下的就是(8)处的超时机制,一次动效收集参与的WindowContainer是有时间限制的,我们需要设置超时的功能,不能让动效一直处于准备状态,那样手机界面就卡死了。这里暂时不深究了,

SyncGroup prepareSyncSet(TransactionReadyListener listener, String name, int method) {
    return new SyncGroup(listener, mNextSyncId++, name, method);//(6)
}

private SyncGroup(TransactionReadyListener listener, int id, String name, int method) {  //(7)
    mSyncId = id;
    mSyncMethod = method;
    mListener = listener;
    mOnTimeout = () -> {   //(8)
        Slog.w(TAG, "Sync group " + mSyncId + " timeout");
        synchronized (mWm.mGlobalLock) {
            onTimeout();
        }
    };
    if (Trace.isTagEnabled(TRACE_TAG_WINDOW_MANAGER)) {
        mTraceName = name + "SyncGroupReady";
        Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, mTraceName, id);
    }
}

接着看applySyncTransaction方法,(5)处就是创建了一个SyncSet,然后到将SyncSet的id和我们通过binder传递过来的Callback放进一个名为mTransactionCallbacksByPendingSyncId的Map容器中,以备使用。
接着到(4)处,这里主要是看单圈的mSyncEngine中是否有激活状态的SyncGroup,因为系统同一时间只能允许一个动效处于激活状态,如果有的话,就只能先入列,等待下个动效流程执行了。
不过这里将会通过applyTransaction方法,将我们应用侧传过来的WindowContainerTransaction对象继续向下执行。

2、 applyTransaction:

private void applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
        @Nullable Transition transition, @NonNull CallerInfo caller,
        @Nullable Transition finishTransition) { //transition = null
    int effects = 0;
    ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
    mService.deferWindowLayout();
    mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
    try {
        if (transition != null && transition.applyDisplayChangeIfNeeded()) { //从入参看,这里执行不到的
            effects |= TRANSACT_EFFECTS_LIFECYCLE;
        }
        //将应用侧传过来的HierarchyOp读出来
        final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
        final int hopSize = hops.size();//获取传过来的HierarchyOp的大小
        ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
        //将迭代器访问应用侧传递过来的WindowContainerTransition中的信息,读出各个窗口需要应用的Change
        Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
                t.getChanges().entrySet().iterator();
        while (entries.hasNext()) { //如果修改的列表中还有数据的话
            //指向下一个迭代器
            final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
            //通过Binder对象获取对应WindowContainer
            final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
            //如果对应的WindowContainer没有被Attached,则结束此次循环
            if (wc == null || !wc.isAttached()) {
                Slog.e(TAG, "Attempt to operate on detached container: " + wc);
                continue;
            }
            // Make sure we add to the syncSet before performing
            // operations so we don't end up splitting effects between the WM
            // pending transaction and the BLASTSync transaction.
            //将当前的WindowContainer,放入id为syncId的SyncGroup当中
            if (syncId >= 0) {
                addToSyncSet(syncId, wc);
            }
            //老规矩这里不用管
            if (transition != null) transition.collect(wc);
            //这里是前文聊到的如果解析WindowContainerTransaction中携带的信息,在修改的时候,修改了哪个属性就会
            //将其对应的标志位置为1,所以这里直接看是否修改CHANGE_FORCE_NO_PIP属性
            if ((entry.getValue().getChangeMask()
                    & WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) {
                // Disable entering pip (eg. when recents pretends to finish itself)
                //不过这两个值都是null,所以好像也没进行啥处理就出了这个if语句
                if (finishTransition != null) {
                    finishTransition.setCanPipOnFinish(false /* canPipOnFinish */);
                } else if (transition != null) {
                    transition.setCanPipOnFinish(false /* canPipOnFinish */);
                }
            }
            // A bit hacky, but we need to detect "remove PiP" so that we can "wrap" the
            // setWindowingMode call in force-hidden.
            boolean forceHiddenForPip = false;
            //如果WindowContainer是Task类型的对象且满足后面的两个条件
            if (wc.asTask() != null && wc.inPinnedWindowingMode()
                    && entry.getValue().getWindowingMode() == WINDOWING_MODE_UNDEFINED) {
                // We are in pip and going to undefined. Now search hierarchy ops to determine
                // whether we are removing pip or expanding pip.
                for (int i = 0; i < hopSize; ++i) {
                    final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
                    if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue;
                    final WindowContainer hopWc = WindowContainer.fromBinder(
                            hop.getContainer());
                    if (!wc.equals(hopWc)) continue;
                    forceHiddenForPip = !hop.getToTop();
                }
            }
            if (forceHiddenForPip) {
                wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
            }
            //这里就是将WindowContainerTransaction中的change信息应用到系统侧的窗口模型中。
            int containerEffect = applyWindowContainerChange(wc, entry.getValue(),
                    t.getErrorCallbackToken());  //(9)
            effects |= containerEffect;

            if (forceHiddenForPip) {
                wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, false /* set */);
            }

            // Lifecycle changes will trigger ensureConfig for everything.
            if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
                    && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
                haveConfigChanges.add(wc);
            }
        }
        // Hierarchy changes
        //这里就是将WindowContainerTransaction中的层次结构的修改应用到系统侧,就是应用reparent类型的修改,
        //修改WindowContainer树下父节点和子节点的关系
        if (hopSize > 0) {
            final boolean isInLockTaskMode = mService.isInLockTaskMode();
            for (int i = 0; i < hopSize; ++i) {
                effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
                        isInLockTaskMode, caller, t.getErrorCallbackToken(),
                        t.getTaskFragmentOrganizer(), finishTransition);    //(10)
            }
        }
        // Queue-up bounds-change transactions for tasks which are now organized. Do
        // this after hierarchy ops so we have the final organized state.
        entries = t.getChanges().entrySet().iterator();
        while (entries.hasNext()) {
            final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
            final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
            if (wc == null || !wc.isAttached()) {
                Slog.e(TAG, "Attempt to operate on detached container: " + wc);
                continue;
            }
            final Task task = wc.asTask();
            final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds();
            if (task == null || !task.isAttached() || surfaceBounds == null) {
                continue;
            }
            if (!task.isOrganized()) {
                final Task parent = task.getParent() != null ? task.getParent().asTask() : null;
                // Also allow direct children of created-by-organizer tasks to be
                // controlled. In the future, these will become organized anyways.
                if (parent == null || !parent.mCreatedByOrganizer) {
                    throw new IllegalArgumentException(
                            "Can't manipulate non-organized task surface " + task);
                }
            }
            final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
            final SurfaceControl sc = task.getSurfaceControl();
            sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);
            if (surfaceBounds.isEmpty()) {
                sft.setWindowCrop(sc, null);
            } else {
                sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());
            }
            task.setMainWindowSizeChangeTransaction(sft);
        }
        if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
            mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
            // Already calls ensureActivityConfig
            mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
            mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
        } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
            final PooledConsumer f = PooledLambda.obtainConsumer(
                    ActivityRecord::ensureActivityConfiguration,
                    PooledLambda.__(ActivityRecord.class), 0,
                    true /* preserveWindow */);
            try {
                for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
                    haveConfigChanges.valueAt(i).forAllActivities(f);
                }
            } finally {
                f.recycle();
            }
        }

        if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
            mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
        }
    } finally {
        mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
        mService.continueWindowLayout();
    }
}

接下来的applyTransaction方法比较长,这里主要将其拆为以下几块:(此处参考博客

1)、Change相关:

根据上面的注释,这里主要看到(9)处这个函数applyWindowContainerChange方法,这个方法根据传入的WindowContainer的类型分为以下四种处理方式,因为前面WindowContainerTransaction详解的博客也说过,传入的WindowContainer只有Task、TaskFragment和DisPlayArea三个类型。所以其实也就前面三个if语句有用。我们就找一个DisplayArea类型为例,讲解下怎么应用的修改吧。

private int applyWindowContainerChange(WindowContainer wc,
        WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) {
//这里直接查看wc是否为DisPlayArea或者TaskFragment的子类,如果不是直接抛出异常
    sanitizeWindowContainer(wc); 
    if (wc.asDisplayArea() != null) {
        return applyDisplayAreaChanges(wc.asDisplayArea(), c);
    } else if (wc.asTask() != null) {
        return applyTaskChanges(wc.asTask(), c);
    } else if (wc.asTaskFragment() != null) {
        return applyTaskFragmentChanges(wc.asTaskFragment(), c, errorCallbackToken);
    } else {
        return applyChanges(wc, c, errorCallbackToken);
    }
}

private int applyDisplayAreaChanges(DisplayArea displayArea,
        WindowContainerTransaction.Change c) {
    final int[] effects = new int[1];
    effects[0] = applyChanges(displayArea, c, null /* errorCallbackToken */);  //(11)

    if ((c.getChangeMask()
            & WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
        if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
            effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
        }
    }

    displayArea.forAllTasks(task -> {
        Task tr = (Task) task;
        if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
            if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
                effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
            }
        }
    });

    return effects[0];
}

这里先插一个小知识点,有没有注意到这个effects数组其实容量就是1,那为什么不直接设置为int型的对象呢?非要设置一个int型的数组?这个问题比较巧妙,且看这个applyDisplayAreaChanges方法中的lambda表达式,这里引用了effects[0],并对其进行了赋值,而lambda表达式中引用的外部变量必须是final类型的,不过final类型的对象只能进行一次赋值,那这里使用int型的数组,就完美的解决了这个问题,虽然我们修改了effects[0]的值,但是java函数是值传递的模式,传递的是effects这个数组的地址值,我们只是修改了这个地址中存储的数据,所以并没有修改这个final类型的数组。这块后面也会加一个小模块讲解一下。

如上代码,其实整个applyDisplayAreaChanges方法的核心就是调用了(11)处的applyChanges方法,而applyChanges方法也是比较简单,就是通过(12)处调用的setTo方法将应用侧传递过来的WindowContainerTransaction对象中包含的修改,通过与运算确定应用侧修改的哪些参数信息,然后在系统侧对相应的窗口应用这些修改,而我们的修改就是修改hidden属性,如代码所示就是将hidden相关的属性赋值过来。

private int applyChanges(WindowContainer<?> container,
        WindowContainerTransaction.Change change, @Nullable IBinder errorCallbackToken) {
    // The "client"-facing API should prevent bad changes; however, just in case, sanitize
    // masks here.
    final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
    final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
    int effects = 0;
    final int windowingMode = change.getWindowingMode();
    if (configMask != 0) {

        adjustBoundsForMinDimensionsIfNeeded(container, change, errorCallbackToken);

        if (windowingMode > -1 && windowingMode != container.getWindowingMode()) {
            // Special handling for when we are setting a windowingMode in the same transaction.
            // Setting the windowingMode is going to call onConfigurationChanged so we don't
            // need it called right now. Additionally, some logic requires everything in the
            // configuration to change at the same time (ie. surface-freezer requires bounds
            // and mode to change at the same time).
            final Configuration c = container.getRequestedOverrideConfiguration();
            c.setTo(change.getConfiguration(), configMask, windowMask);  //(12)
        } else {
            final Configuration c =
                    new Configuration(container.getRequestedOverrideConfiguration());
            c.setTo(change.getConfiguration(), configMask, windowMask);
            container.onRequestedOverrideConfigurationChanged(c);
        }
        effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
        if (windowMask != 0 && container.isEmbedded()) {
            // Changing bounds of the embedded TaskFragments may result in lifecycle changes.
            effects |= TRANSACT_EFFECTS_LIFECYCLE;
        }
    }
    if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
        if (container.setFocusable(change.getFocusable())) {
            effects |= TRANSACT_EFFECTS_LIFECYCLE;
        }
    }

    if (windowingMode > -1) {
        if (mService.isInLockTaskMode()
                && WindowConfiguration.inMultiWindowMode(windowingMode)) {
            throw new UnsupportedOperationException("Not supported to set multi-window"
                    + " windowing mode during locked task mode.");
        }

        if (windowingMode == WindowConfiguration.WINDOWING_MODE_PINNED) {
            // Do not directly put the container into PINNED mode as it may not support it or
            // the app may not want to enter it. Instead, send a signal to request PIP
            // mode to the app if they wish to support it below in #applyTaskChanges.
            return effects;
        }

        final int prevMode = container.getWindowingMode();
        container.setWindowingMode(windowingMode);
        if (prevMode != container.getWindowingMode()) {
            // The activity in the container may become focusable or non-focusable due to
            // windowing modes changes (such as entering or leaving pinned windowing mode),
            // so also apply the lifecycle effects to this transaction.
            effects |= TRANSACT_EFFECTS_LIFECYCLE;
        }
    }
    return effects;
}
public void setTo(@NonNull Configuration delta, @Config int mask,
        @WindowConfiguration.WindowConfig int windowMask) {
   ………………………………………………………………
    if ((mask & ActivityInfo.CONFIG_KEYBOARD_HIDDEN) != 0) {
        keyboardHidden = delta.keyboardHidden;
        hardKeyboardHidden = delta.hardKeyboardHidden;
        navigationHidden = delta.navigationHidden;
    }
    ………………………………………………………………
    if ((mask & ActivityInfo.CONFIG_FONT_WEIGHT_ADJUSTMENT) != 0) {
        fontWeightAdjustment = delta.fontWeightAdjustment;
    }
}

2)、HierarchyOp相关:

HierarchyOp相关的修改,主要是修改层级结构的,其实现主要是在(10)处,通过applyHierarchyOp方法进行实现。但是applyHierarchyOp方法实在是过长,总共有二十余种:

public static final int HIERARCHY_OP_TYPE_REPARENT = 0;
public static final int HIERARCHY_OP_TYPE_REORDER = 1;
public static final int HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT = 2;
public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT = 3;
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS = 4;
public static final int HIERARCHY_OP_TYPE_LAUNCH_TASK = 5;
public static final int HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT = 6;
public static final int HIERARCHY_OP_TYPE_CREATE_TASK_FRAGMENT = 7;
public static final int HIERARCHY_OP_TYPE_DELETE_TASK_FRAGMENT = 8;
public static final int HIERARCHY_OP_TYPE_START_ACTIVITY_IN_TASK_FRAGMENT = 9;
public static final int HIERARCHY_OP_TYPE_REPARENT_ACTIVITY_TO_TASK_FRAGMENT = 10;
public static final int HIERARCHY_OP_TYPE_REPARENT_CHILDREN = 11;
public static final int HIERARCHY_OP_TYPE_PENDING_INTENT = 12;
public static final int HIERARCHY_OP_TYPE_SET_ADJACENT_TASK_FRAGMENTS = 13;
public static final int HIERARCHY_OP_TYPE_START_SHORTCUT = 14;
public static final int HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER = 15;
public static final int HIERARCHY_OP_TYPE_ADD_RECT_INSETS_PROVIDER = 16;
public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_PROVIDER = 17;
public static final int HIERARCHY_OP_TYPE_REQUEST_FOCUS_ON_TASK_FRAGMENT = 18;
public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 19;
public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 20;
public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 21;
public static final int HIERARCHY_OP_TYPE_SET_COMPANION_TASK_FRAGMENT = 22;
public static final int HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH = 23;
public static final int HIERARCHY_OP_TYPE_SET_TASK_FRAGMENT_OPERATION = 24;

于是我们就节选一个操作进行解读:

private int applyHierarchyOp(WindowContainerTransaction.HierarchyOp hop, int effects,
        int syncId, @Nullable Transition transition, boolean isInLockTaskMode,
        @NonNull CallerInfo caller, @Nullable IBinder errorCallbackToken,
        @Nullable ITaskFragmentOrganizer organizer, @Nullable Transition finishTransition) {
final int type = hop.getType();
switch (type) {
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
    effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId,
            isInLockTaskMode);
    break;
}
}

此场景又调用了reparentChildrenTasksHierarchyOp方法,再找到该方法:

private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop,
        @Nullable Transition transition, int syncId, boolean isInLockTaskMode) {
    WindowContainer<?> currentParent = hop.getContainer() != null
            ? WindowContainer.fromBinder(hop.getContainer()) : null;
    WindowContainer newParent = hop.getNewParent() != null
            ? WindowContainer.fromBinder(hop.getNewParent()) : null;
    if (currentParent == null && newParent == null) {
        throw new IllegalArgumentException("reparentChildrenTasksHierarchyOp: " + hop);
    } else if (currentParent == null) {
        currentParent = newParent.asTask().getDisplayContent().getDefaultTaskDisplayArea();
    } else if (newParent == null) {
        newParent = currentParent.asTask().getDisplayContent().getDefaultTaskDisplayArea();
    }

    if (currentParent == newParent) {
        Slog.e(TAG, "reparentChildrenTasksHierarchyOp parent not changing: " + hop);
        return 0;
    }
    if (!currentParent.isAttached()) {
        Slog.e(TAG, "reparentChildrenTasksHierarchyOp currentParent detached="
                + currentParent + " hop=" + hop);
        return 0;
    }
    if (!newParent.isAttached()) {
        Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent detached="
                + newParent + " hop=" + hop);
        return 0;
    }
    if (newParent.inPinnedWindowingMode()) {
        Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent in PIP="
                + newParent + " hop=" + hop);
        return 0;
    }

    final boolean newParentInMultiWindow = newParent.inMultiWindowMode();
    final TaskDisplayArea newParentTda = newParent.asTask() != null
            ? newParent.asTask().getDisplayArea()
            : newParent.asTaskDisplayArea();
    final WindowContainer finalCurrentParent = currentParent;
    final WindowContainer finalNewParent = newParent;
    Slog.i(TAG, "reparentChildrenTasksHierarchyOp"
            + " currentParent=" + currentParent + " newParent=" + newParent + " hop=" + hop);

    // We want to collect the tasks first before re-parenting to avoid array shifting on us.
    final ArrayList<Task> tasksToReparent = new ArrayList<>();

    currentParent.forAllTasks(task -> {
        Slog.i(TAG, " Processing task=" + task);
        final boolean reparent;
        if (task.mCreatedByOrganizer || task.getParent() != finalCurrentParent) {
            // We only care about non-organized task that are direct children of the thing we
            // are reparenting from.
            return false;
        }
        if (newParentInMultiWindow && !task.supportsMultiWindowInDisplayArea(newParentTda)) {
            Slog.e(TAG, "reparentChildrenTasksHierarchyOp non-resizeable task to multi window,"
                    + " task=" + task);
            return false;
        }
        if (!ArrayUtils.isEmpty(hop.getActivityTypes())
                && !ArrayUtils.contains(hop.getActivityTypes(), task.getActivityType())) {
            return false;
        }
        if (!ArrayUtils.isEmpty(hop.getWindowingModes())
                && !ArrayUtils.contains(hop.getWindowingModes(), task.getWindowingMode())) {
            return false;
        }
        if (isLockTaskModeViolation(finalNewParent, task, isInLockTaskMode)) {
            return false;
        }

        if (hop.getToTop()) {
            tasksToReparent.add(0, task);
        } else {
            tasksToReparent.add(task);
        }
        return hop.getReparentTopOnly() && tasksToReparent.size() == 1;
    });

    final int count = tasksToReparent.size();
    for (int i = 0; i < count; ++i) {
        final Task task = tasksToReparent.get(i);
        if (syncId >= 0) {
            addToSyncSet(syncId, task);
        }
        if (transition != null) transition.collect(task);

        if (newParent instanceof TaskDisplayArea) {
            // For now, reparenting to display area is different from other reparents...
            task.reparent((TaskDisplayArea) newParent, hop.getToTop());
        } else {
            task.reparent((Task) newParent,
                    hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
                    false /*moveParents*/, "processChildrenTaskReparentHierarchyOp");
        }
    }

    if (transition != null) transition.collect(newParent);

    return TRANSACT_EFFECTS_LIFECYCLE;
}

reparentChildrenTasksHierarchyOp方法的功能其实比较简单,就是从currentParent中筛选处所有可以进行reparent操作的Task,全部添加到tasksToReparent之中,然后再将tasksToReparent中所有的Task全部reparent到newParent下面。

3)、setBoundsChangeTransaction相关:

这块对应的就是此处的代码:

entries = t.getChanges().entrySet().iterator();
while (entries.hasNext()) {
    final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();
    final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());
    if (wc == null || !wc.isAttached()) {
        Slog.e(TAG, "Attempt to operate on detached container: " + wc);
        continue;
    }
    final Task task = wc.asTask();
    final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds();
    if (task == null || !task.isAttached() || surfaceBounds == null) {
        continue;
    }
    if (!task.isOrganized()) {
        final Task parent = task.getParent() != null ? task.getParent().asTask() : null;
        // Also allow direct children of created-by-organizer tasks to be
        // controlled. In the future, these will become organized anyways.
        if (parent == null || !parent.mCreatedByOrganizer) {
            throw new IllegalArgumentException(
                    "Can't manipulate non-organized task surface " + task);
        }
    }
    final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();
//获取task对应的SurfaceControl
    final SurfaceControl sc = task.getSurfaceControl();
//设置Transaction
    sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);
    if (surfaceBounds.isEmpty()) {
//设置task对应的SurfaceControl的边界
        sft.setWindowCrop(sc, null);
    } else {
        sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());
    }
    task.setMainWindowSizeChangeTransaction(sft);
}

/**
 * See {@link WindowContainerTransaction#setBoundsChangeTransaction}. In short this
 * transaction will be consumed by the next BASE_APPLICATION window within our hierarchy
 * to resize, and it will defer the transaction until that resize frame completes.
 */

void setMainWindowSizeChangeTransaction(SurfaceControl.Transaction t) {
    setMainWindowSizeChangeTransaction(t, this);
    forAllWindows(WindowState::requestRedrawForSync, true);
}

这块的代码中,直接使用了task.asTask()方法,也就是说其实是针对task类型的WindowContainer设计的。主要操作也都在代码中注释了,就是获取task的SurfaceControl,然后通过Transition对这个SurfaceControl的边界进行设置,这块的具体原理后面会讲到,这里就不做过多深究了,等后面讲到SurfaceControl的时候再详细讲解,不过最后的setMainWindowSizeChangeTransaction方法没太看明白。

4)、更新Activity的可见性和Configuration。

这块代码主要是根据前面的修改中,计算得到的effects判断是否有生命周期的更新,如果有生命周期的更新或者configuration的更新就需要调用下面的函数:

if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
    mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
    // Already calls ensureActivityConfig
    mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
    mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
} else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
    final PooledConsumer f = PooledLambda.obtainConsumer(
            ActivityRecord::ensureActivityConfiguration,
            PooledLambda.__(ActivityRecord.class), 0,
            true /* preserveWindow */);
    try {
        for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
            haveConfigChanges.valueAt(i).forAllActivities(f);
        }
    } finally {
        f.recycle();
    }
}

if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
    mService.addWindowLayoutReasons(LAYOUT_REASON_CONFIG_CHANGED);
}

如果有生命周期的更新就重新更新系统中的所有Activity的可见性和Configuration,并且寻找新的top Task和resume Activty。如果没有生命周期的更新,只有configuration的更新,就只针对这些发生了Configuration更新的WindowContainer,遍历其下的所有子ActivityRecord,调用ActivityRecord#ensureActivityConfiguration进行Configuration相关的更新。

3、 总结:
其实这节的主要内容就是WindowContainerTransaction的解码任务,将应用侧打包发送过来的WindowContainerTransaction对象进行解析,然后应用到系统侧维护的窗口数据库中。分别通过其中Change、HierarchyOp和BoundsChange三个修改进行处理。当然这里涉及的知识也比较多,有前面两节相关的内容,还有一些新的知识点,我这里都没展开去讲,比如SyncSet、SyncGroup、SurfaceControl等内容,这些内容也很复杂需要通过专门的章节进行讲解。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值