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等内容,这些内容也很复杂需要通过专门的章节进行讲解。