前文
Android U 多任务启动分屏——SystemUI流程
前文分屏中说到Transitions的startTransition方法中,通过mOrganizer.startNewTransition(type, wct);提交WindowContainerTransaction相关事务到system_server侧,这里以此流程为例,讲解WindowOrganizerController是如何处理WindowContainerTransaction的。
system_server侧事务处理流程
systemui跨进程通信到system_server
代码路径:frameworks/base/core/java/android/window/WindowOrganizer.java
/**
* Starts a new transition, don't use this to start an already created one.
* @param type The type of the transition. This is ignored if a transitionToken is provided.
* @param t The set of window operations that are part of this transition.
* @return A token identifying the transition. This will be the same as transitionToken if it
* was provided.
* @hide
*/
@RequiresPermission(android.Manifest.permission.MANAGE_ACTIVITY_TASKS)
@NonNull
public IBinder startNewTransition(int type, @Nullable WindowContainerTransaction t) {
try {
return getWindowOrganizerController().startNewTransition(type, t);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
static IWindowOrganizerController getWindowOrganizerController() {
return IWindowOrganizerControllerSingleton.get();
}
这里可以看出getWindowOrganizerController()就是获取IWindowOrganizerController对象,调用其startNewTransition(type, t)方法,其中参数type为systemui侧传递的TRANSIT_TO_FRONT(值为3),t则是systemui侧传递的WindowContainerTransaction对象。
代码路径:frameworks/base/core/java/android/window/IWindowOrganizerController.aidl
interface IWindowOrganizerController {
......
/**
* Starts a new transition.
* @param type The transition type.
* @param t Operations that are part of the transition.
* @return a token representing the transition.
*/
IBinder startNewTransition(int type, in @nullable WindowContainerTransaction t);
找到其aidl接口,接下来找到其实现类
/**
* Server side implementation for the interface for organizing windows
* @see android.window.WindowOrganizer
*/
class WindowOrganizerController extends IWindowOrganizerController.Stub
implements BLASTSyncEngine.TransactionReadyListener {
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
@Override
public IBinder startNewTransition(int type, @Nullable WindowContainerTransaction t) {
return startTransition(type, null /* transitionToken */, t);
}
最终调用到WindowOrganizerController的startNewTransition方法,该方法就是调用一个startTransition方法,这个方法中传递了type(值为TRANSIT_TO_FRONT,即3)、transitionToken(值为null)以及WindowContainerTransaction对象。
处理动画并提交事务
private IBinder startTransition(@WindowManager.TransitionType int type,
@Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t) {
//检查MANAGE_ACTIVITY_TASKS权限
enforceTaskPermission("startTransition()");
//获取调用方法的pid和uid
final CallerInfo caller = new CallerInfo();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
//transitionToken为空,因此transition为空
Transition transition = Transition.fromBinder(transitionToken);
//transition为空不满足条件,因此不走该逻辑
if (mTransitionController.getTransitionPlayer() == null && transition == null) {
Slog.w(TAG, "Using shell transitions API for legacy transitions.");
if (t == null) {
throw new IllegalArgumentException("Can't use legacy transitions in"
+ " compatibility mode with no WCT.");
}
//如果mTransitionController.getTransitionPlayer()和transition
//均为空,则直接调用applyTransaction方法
applyTransaction(t, -1 /* syncId */, null, caller);
return null;
}
//判断当前传递过来的WindowContainerTransaction对象t是否为空
//不为空则赋值给wct,为空则重新创建一个WindowContainerTransaction对象。
final WindowContainerTransaction wct =
t != null ? t : new WindowContainerTransaction();
//transition为空,走此处逻辑
if (transition == null) {
if (type < 0) {
throw new IllegalArgumentException("Can't create transition with no type");
}
// This is a direct call from shell, so the entire transition lifecycle is
// contained in the provided transaction if provided. Thus, we can setReady
// immediately after apply.
final boolean needsSetReady = t != null;
final Transition nextTransition = new Transition(type, 0 /* flags */,
mTransitionController, mService.mWindowManager.mSyncEngine);
nextTransition.calcParallelCollectType(wct);
mTransitionController.startCollectOrQueue(nextTransition,
//deferred表示这个Transition的启动是否被推迟,
//startCollectOrQueue方法中传递的值为false
(deferred) -> {
//动画状态为STATE_STARTED
nextTransition.start();
nextTransition.mLogger.mStartWCT = wct;
applyTransaction(wct, -1 /* syncId */, nextTransition, caller,
deferred);
if (needsSetReady) {
nextTransition.setAllReady();
}
});
return nextTransition.getToken();
}
// The transition already started collecting before sending a request to shell,
// so just start here.
//如果transition.isCollecting()和transition.isForcePlaying()均为false
if (!transition.isCollecting() && !transition.isForcePlaying()) {
Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
+ " means Shell took too long to respond to a request. WM State may be"
+ " incorrect now, please file a bug");
applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
return transition.getToken();
}
//动画状态为STATE_STARTED
transition.start();
transition.mLogger.mStartWCT = wct;
applyTransaction(wct, -1 /*syncId*/, transition, caller);
// Since the transition is already provided, it means WMCore is determining the
// "readiness lifecycle" outside the provided transaction, so don't set ready here.
return transition.getToken();
}
} finally {
Binder.restoreCallingIdentity(ident);
}
}
我们先顺理一下传递过来的参数值,这有助于我们排查后续的逻辑顺序。
@WindowManager.TransitionType int type:TRANSIT_TO_FRONT(3)
@Nullable IBinder transitionToken:null,空值
@Nullable WindowContainerTransaction t:systemui侧传递过来的WindowContainerTransaction对象,包含bounds、task等相关操作指令。
因此从入参我们可以清楚该方法的逻辑走向
- 如果mTransitionController.getTransitionPlayer()和transition均为空,则直接调用
applyTransaction(t, -1 /* syncId */, null, caller); - 如果transition为空,则创建一个Transition对象,然后通过mTransitionController.startCollectOrQueue调用
applyTransaction(wct, -1 /* syncId */, nextTransition, caller, deferred); - 如果transition.isCollecting()和transition.isForcePlaying()均为false,则直接调用
applyTransaction(t, -1 /* syncId */, null, caller); - 如果上述条件均不满足的情况,则直接通过transition.start()修改动画状态为
STATE_STARTED,后续调用applyTransaction(wct, -1 /*syncId*/, transition, caller);
注:这里我们不关注Shell动画的逻辑,仅关注applyTransaction方法的调用逻辑。
结合传递过来的参数发现,transitionToken为空,因此Transition transition = Transition.fromBinder(transitionToken);这里transition值也为空,所以我们这里走的是第二步逻辑,也就是applyTransaction(wct, -1 /* syncId */, nextTransition, caller, deferred);。
其中参数:
-
wct,systemui侧传递过来的WindowContainerTransaction对象 -
-1,一个syncId -
nextTransition,新创建的shell动画 -
caller,是CallerInfo对象,这个主要就是用来记录Binder调用方的pid和uidstatic class CallerInfo { final int mPid; final int mUid; CallerInfo() { mPid = Binder.getCallingPid(); mUid = Binder.getCallingUid(); } }我们这里调用方是SystemUI,因此
caller记录的就是SystemUI的pid和uid。 -
deferred,表示这个Transition的启动是否被推迟,我们可以从mTransitionController.startCollectOrQueue方法中知道该值为false/** Returns {@code true} if it started collecting, {@code false} if it was queued. */ boolean startCollectOrQueue(Transition transit, OnStartCollect onStartCollect) { ...... //首先将当前Transition的状态标记为STATE_COLLECTING,接着通过BLASTSyncEngine.startSyncSet方法,创建一个SyncGroup,用来收集动画的参与者 moveToCollecting(transit); //传递deferred值为false onStartCollect.onCollectStarted(false /* deferred */); return true; }
这里我们继续跟踪applyTransaction方法。
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller, boolean deferred) {
if (deferred) {
try {
return applyTransaction(t, syncId, transition, caller);
} catch (RuntimeException e) {
// If the transaction is deferred, the caller could be from TransitionController
// #tryStartCollectFromQueue that executes on system's worker thread rather than
// binder thread. And the operation in the WCT may be outdated that violates the
// current state. So catch the exception to avoid crashing the system.
Slog.e(TAG, "Failed to execute deferred applyTransaction", e);
}
return TRANSACT_EFFECTS_NONE;
}
return applyTransaction(t, syncId, transition, caller);
}
这里根据前面传递的参数deferred,决定了走哪个分支,deferred仅表示这个Transition的启动是否被推迟,对我们这里分析并无影响(涉及shell动画流程),最终都会走到以下方法中:
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller) {
return applyTransaction(t, syncId, transition, caller, null /* finishTransition */);
}
继上述参数的传递多了一个finishTransition参数,这里的值就是null,继续跟踪这个重载的applyTransaction方法。
处理事务
/**
* @param syncId If non-null, this will be a sync-transaction.
* @param transition A transition to collect changes into.
* @param caller Info about the calling process.
* @param finishTransition The transition that is currently being finished.
* @return The effects of the window container transaction.
*/
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,
@Nullable Transition transition, @NonNull CallerInfo caller,
@Nullable Transition finishTransition) {
int effects = TRANSACT_EFFECTS_NONE;
ProtoLog.v(WM_DEBUG_WINDOW_ORGANIZER, "Apply window transaction, syncId=%d", syncId);
mService.deferWindowLayout();
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(true /* deferUpdate */);
try {
//1.动画处理
if (transition != null) {
transition.applyDisplayChangeIfNeeded();
}
//2.WindowContainerTransaction.Change处理
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
final int hopSize = hops.size();
final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> 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;
}
// 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.
if (syncId >= 0) {
addToSyncSet(syncId, wc);
}
if (transition != null) transition.collect(wc);
if ((entry.getValue().getChangeMask()
& WindowContainerTransaction.Change.CHANGE_FORCE_NO_PIP) != 0) {
// Disable entering pip (eg. when recents pretends to finish itself)
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;
//判断PIP(画中画)是否在顶端
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);
//找到hop类型问题为HIERARCHY_OP_TYPE_REORDER的操作,不是则跳过
if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue;
final WindowContainer hopWc = WindowContainer.fromBinder(
hop.getContainer());
if (!wc.equals(hopWc)) continue;
//getToTop表示获取reparent操作后是否需要将子WindowContainer移动到父WindowContainer的top。
//这里取是reparent操作后不用将子WindowContainer移动到父WindowContainer的top。
forceHiddenForPip = !hop.getToTop();
}
}
//隐藏PIP(画中画)
if (forceHiddenForPip) {
wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);
// When removing pip, make sure that onStop is sent to the app ahead of
// onPictureInPictureModeChanged.
// See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismiss
wc.asTask().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
wc.asTask().mTaskSupervisor.processStoppingAndFinishingActivities(
null /* launchedActivity */, false /* processPausingActivities */,
"force-stop-on-removing-pip");
}
//获取一个的change,取出change中包裹的具体WindowContainer,即上屏和下屏的对应Task,
//然后调用applyWindowContainerChange进行对应处理
int containerEffect = applyWindowContainerChange(wc, entry.getValue(),
t.getErrorCallbackToken());
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);
}
}
//3.WindowContainerTransaction.HierarchyOp处理
// Hierarchy changes
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);
}
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
// this after hierarchy ops so we have the final organized state.
//4.Change.mBoundsChangeSurfaceBounds处理
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);
}
//5.更新Activity的可见性和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) {
for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
haveConfigChanges.valueAt(i).forAllActivities(r -> {
r.ensureActivityConfiguration(0, PRESERVE_WINDOWS);
});
}
}
if (effects != 0) {
mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
} finally {
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
mService.continueWindowLayout();
}
return effects;
}
这个方法总体可以分为三个部分: 1.transition动画处理 2.WindowContainerTransaction.Change处理 3.WindowContainerTransaction.HierarchyOp处理 4.Change.mBoundsChangeSurfaceBounds处理 5.更新Activity的可见性和Configuration
transition动画处理
if (transition != null) {
transition.applyDisplayChangeIfNeeded();
}
这里transition由前面传递的值可以知道其不为空,调用了Transition的applyDisplayChangeIfNeeded方法。
代码路径:frameworks/base/services/core/java/com/android/server/wm/Transition.java
/** Applies the new configuration for the changed displays. */
void applyDisplayChangeIfNeeded() {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
final WindowContainer<?> wc = mParticipants.valueAt(i);
final DisplayContent dc = wc.asDisplayContent();
if (dc == null || !mChanges.get(dc).hasChanged()) continue;
dc.sendNewConfiguration();
// Set to ready if no other change controls the ready state. But if there is, such as
// if an activity is pausing, it will call setReady(ar, false) and wait for the next
// resumed activity. Then do not set to ready because the transition only contains
// partial participants. Otherwise the transition may only handle HIDE and miss OPEN.
if (!mReadyTracker.mUsed) {
setReady(dc, true);
}
}
}
只有setReady为true的时候,sendNewConfiguration才会执行,这个方法后续做一些配置上的处理,最后通过performSurfacePlacement处理窗口布局。
WindowContainerTransaction.Change处理
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
final int hopSize = hops.size();
final ArraySet<WindowContainer<?>> haveConfigChanges = new ArraySet<>();
Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> 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;
}
// 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.
if (syncId >= 0) {
addToSyncSet(syncId, wc);
}
if (transition != null) transition.collect(wc);
......
//获取一个的change,取出change中包裹的具体WindowContainer,即上屏和下屏的对应Task,
//然后调用applyWindowContainerChange进行对应处理
int containerEffect = applyWindowContainerChange(wc, entry.getValue(),
t.getErrorCallbackToken());
effects |= containerEffect;
......
// Lifecycle changes will trigger ensureConfig for everything.
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
&& (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
haveConfigChanges.add(wc);
}
}
这里我们省略了画中画相关部分,仅看这段代码的关键部分。 这部分的主要工作是,遍历前面传入的WindowContainerTransaction的mChanges(比如:在分屏时,SystemUI侧调用setBounds调整Task大小)并操作,总体概括为这几件事:
-
取出在WindowContainerTransaction中的mChanges集合,根据里面的WindowContainerToken转化为WindowContainer。
final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next(); final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());这里
entry的key和value其实就是,容器的token以及其对应的Change。 在多任务分屏启动流程中,调用updateWindowBounds方法用来更新上下分屏的bounds,其中的容器指的就是SideStage和MainStage这两个分屏task,把他们需要修改的bounds保存在了WindowContainerTransaction中的mChanges集合中,这里就是把它们取出来进行处理。 -
调用addToSyncSet将当前WindowContainer添加到同步集合中,
syncId是前面传递过来的-1,因此这里流程不会走。// 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. if (syncId >= 0) { addToSyncSet(syncId, wc); } -
调用applyWindowContainerChange去应用Change里的设置,并将返回结果赋值给
effects。int containerEffect = applyWindowContainerChange(wc, entry.getValue(), t.getErrorCallbackToken()); effects |= containerEffect;后面会详细分析applyWindowContainerChange方法(见【WindowOrganizerController侧Change处理】)。
-
如果本次修改没有影响到TRANSACT_EFFECTS_LIFECYCLE,但是对TRANSACT_EFFECTS_CLIENT_CONFIG产生了影响,那么将当前WindowContainer添加到haveConfigChanges队列中,该队列保存了所有Configuration发生改变的WindowContainer。
// Lifecycle changes will trigger ensureConfig for everything. if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) { haveConfigChanges.add(wc); }effects的值依赖前面applyWindowContainerChange方法的返回结果,haveConfigChanges为后续确认Configuration的正确性做准备。
WindowContainerTransaction.HierarchyOp处理
// Hierarchy changes
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);
}
}
处理Task的一些相关操作,比如:在分屏时,SystemUI侧调用的startTask、reroderTask和reparent等。 后面会详细分析applyHierarchyOp方法(见【WindowOrganizerController侧Task相关操作处理】)。
Change.mBoundsChangeSurfaceBounds处理
// 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();
//获取mBoundsChangeSurfaceBounds
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);
}
}
//对task的Surface进行处理
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);
}
这个方法同样是遍历Change,这里主要就是对Change中保存的mBoundsChangeSurfaceBounds进行处理。针对Surface的操作,这里不做重点。
更新Activity的可见性和Configuration
//是否有生命周期相关的更新
if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
//是否延迟更新容器可见性,这里传递的是false
mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);
// Already calls ensureActivityConfig
//重新更新系统中的所有Activity的可见性和Configuration
mService.mRootWindowContainer.ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);
//寻找新的top Task和resume Activty
mService.mRootWindowContainer.resumeFocusedTasksTopActivities();
// 在没有生命周期相关方面的更新的情况,
// 判断是否只有Configuration相关的更新
} else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
//针对这些发生了Configuration更新的WindowContainer,
//遍历其下的所有子ActivityRecord,调用ActivityRecord.ensureActivityConfiguration进行Configuration相关的更新
haveConfigChanges.valueAt(i).forAllActivities(r -> {
r.ensureActivityConfiguration(0, PRESERVE_WINDOWS);
});
}
}
//判断effects是否为0
if (effects != 0) {
//更新窗口布局
mService.mWindowManager.mWindowPlacerLocked.requestTraversal();
}
简单来说,这个方法根据不同条件更新Activity的可见性和Configuration。
haveConfigChanges就是前面处理Change的流程中通过haveConfigChanges.add(wc);添加了相关容器,这里调用ensureActivityConfiguration用来确定该容器Configuration的正确性。
WindowContainerTransaction事务处理
WindowContainerTransaction的发送都是通过WindowOrganizer的相关方法,最终跨进程将WindowContainerTransaction发送给了服务端的WindowOrganizerController。
WindowContainerToken介绍
WindowContainerTransaction中许多操作,是需要传递WindowContainerToken的。 如果想通过WindowContainerTransaction的方法修改某个WindowContainer的属性,必须传入该WindowContainer对应的token(拥有户口信息的居民才是合法居民)。 因此在了解WindowContainerTransaction之前我们先简单了解下WindowContainerToken。
WindowContainerToken
代码路径:frameworks/base/core/java/android/window/WindowContainerToken.java
/**
* 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();
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStrongBinder(mRealToken.asBinder());
}
@NonNull
public static final Creator<WindowContainerToken> CREATOR =
new Creator<WindowContainerToken>() {
@Override
public WindowContainerToken createFromParcel(Parcel in) {
return new WindowContainerToken(in);
}
@Override
public WindowContainerToken[] newArray(int size) {
return new WindowContainerToken[size];
}
};
......
}
WindowContainterToken实现了Parcelable,因此可以跨进程传递。
简单来说,WindowContainerToken可以看做是IBinder类型的token的封装,这个token通过asBinder方法返回。
这里的成员变量mRealToken是IWindowContainerToken对象,其作用也就是作为一个Token。
代码路径:frameworks/base/core/java/android/window/IWindowContainerToken.aidl
/**
* Interface for a window container to communicate with the window manager. This also acts as a
* token.
* @hide
*/
interface IWindowContainerToken {
}
从代码中可以看到,其实就是空的aidl接口,如前面所说的只作为Token。
再看看在哪里会创建这个WindowContainterToken
我们可以发现当前是在TaskInfo和DisplayAreaInfo有使用到,至少能说明Task容器和DisplayArea容器都拥有WindowContainterToken。
下面再来看看IWindowContainerToken的实现。
RemoteToken
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
static class RemoteToken extends IWindowContainerToken.Stub {
final WeakReference<WindowContainer> mWeakRef;
private WindowContainerToken mWindowContainerToken;
RemoteToken(WindowContainer container) {
mWeakRef = new WeakReference<>(container);
}
@Nullable
WindowContainer getContainer() {
return mWeakRef.get();
}
static RemoteToken fromBinder(IBinder binder) {
return (RemoteToken) binder;
}
WindowContainerToken toWindowContainerToken() {
if (mWindowContainerToken == null) {
mWindowContainerToken = new WindowContainerToken(this);
}
return mWindowContainerToken;
}
......
}
RemoteToken是IWindowContainerToken的实现,这里我们可以看到通过弱引用的方式,关联了WindowContainerToken。即通过RemoteToken关联WindowContainer和WindowContainerToken。
也就是说,mRealToken其实就是一个RemoteToken类型的对象,它才是能够通过弱引用真正的指向WindowContainer,同时还包含WindowContainerToken属性。
再来查找下在哪里使用到了RemoteToken:
从代码中查找可以发现,Task、TaskFragment和DisplayArea均拥有RemoteToken。
即Task、TaskFragment和DisplayArea这个几个容器,可以使用WindowContainerTransaction的方式进行Change的相关操作。
WindowContainerTransaction介绍
代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java
/**
* Represents a collection of operations on some WindowContainers that should be applied all at
* once.
*
* @hide
*/
@TestApi
public final class WindowContainerTransaction implements Parcelable {
//Change集合,用来保存容器中一些属性的修改
private final ArrayMap<IBinder, Change> mChanges = new ArrayMap<>();
// Flat list because re-order operations are order-dependent
//HierarchyOp集合,用来保存容器层级的操作
private final ArrayList<HierarchyOp> mHierarchyOps = new ArrayList<>();
WindowContainerTransaction是应用在WindowContainer上的操作集合,其实现了Parcelable为其在系统服务端和App端之间的传输提供了支持。
其最重要的就是Change和HierarchyOp,这里我们可以看到mChanges集合中IBinder和Change是一一对应的。
Change
用来保存容器中一些属性的修改,之后通过mChanges取出。
Change的获取
private Change getOrCreateChange(IBinder token) {
Change out = mChanges.get(token);
if (out == null) {
out = new Change();
mChanges.put(token, out);
}
return out;
}
通过Ibinder对象的传递来获取对应的Change,调用该方法时,这里Ibinder对象token传递的其实就是WindowContainerToken对象的asBinder()。
也就是说,这个方法就是通过传递过来的不同的容器所对应的WindowContainerToken,然后找到其对应着的Change;如果这个Change为空则创建一个Change,并将其放到mChanges集合中,使WindowContainerToken和Change一一对应。
Change的定义
/**
* Holds changes on a single WindowContainer including Configuration changes.
* @hide
*/
public static class Change implements Parcelable {
......
private final Configuration mConfiguration = new Configuration();
private boolean mFocusable = true;
private boolean mHidden = false;
private boolean mIgnoreOrientationRequest = false;
private boolean mForceTranslucent = false;
private boolean mDragResizing = 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;
@Nullable
private Rect mRelativeBounds = null;
private int mActivityWindowingMode = -1;
private int mWindowingMode = -1;
这里面的一些成员变量都是可以修改属性,其中最为常用的就是Configuration。
WindowContainerTransaction并不支持对Configuration中所有的属性进行修改,主要是screenSize、windowingMode和bounds等。
mChangeMask、mConfigSetMask和mWindowSetMask均为标记,后续有不同类型的修改都会通过该变量进行标记。
常用方法
这些常用方法都会通过mChangeMask、mConfigSetMask和mWindowSetMask这个几个变量在不同的情况设置标记,后续通过这些标记,就可以判断调用过哪些Chane修改。
设置容器Configuration相关属性
//修改容器bounds
public WindowContainerTransaction setBounds(
@NonNull WindowContainerToken container,@NonNull Rect bounds) {
Change chg = getOrCreateChange(container.asBinder());
chg.mConfiguration.windowConfiguration.setBounds(bounds);
chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
return this;
}
//修改应用bounds
/**
* Resize a container's app bounds. This is the bounds used to report appWidth/Height to an
* app's DisplayInfo. It is derived by subtracting the overlapping portion of the navbar from
* the full bounds.
*/
@NonNull
public WindowContainerTransaction setAppBounds(
@NonNull WindowContainerToken container,@NonNull Rect appBounds) {
Change chg = getOrCreateChange(container.asBinder());
chg.mConfiguration.windowConfiguration.setAppBounds(appBounds);
chg.mConfigSetMask |= ActivityInfo.CONFIG_WINDOW_CONFIGURATION;
chg.mWindowSetMask |= WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;
return this;
}
//修改容器屏幕尺寸
/**
* Resize a container's configuration size. The configuration size is what gets reported to the
* app via screenWidth/HeightDp and influences which resources get loaded. This size is
* derived by subtracting the overlapping portions of both the statusbar and the navbar from
* the full bounds.
*/
@NonNull
public WindowContainerTransaction setScreenSizeDp(
@NonNull WindowContainerToken container, int w, int h) {
Change chg = getOrCreateChange(container.asBinder());
chg.mConfiguration.screenWidthDp = w;
chg.mConfiguration.screenHeightDp = h;
chg.mConfigSetMask |= ActivityInfo.CONFIG_SCREEN_SIZE;
return this;
}
//修改容器densityDpi
/**
* Sets the densityDpi value in the configuration for the given container.
* @hide
*/
@NonNull
public WindowContainerTransaction setDensityDpi(@NonNull WindowContainerToken container,
int densityDpi) {
Change chg = getOrCreateChange(container.asBinder());
chg.mConfiguration.densityDpi = densityDpi;
chg.mConfigSetMask |= ActivityInfo.CONFIG_DENSITY;
return this;
}
这些方法都是通过传递的WindowContainerToken,获取对应的Change,然后再通过Configuration对象修改对应的属性值,保存在Change中,并且在mConfigSetMask和mWindowSetMask中保存了修改标记。
设置容器窗口模式
/**
* 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;
}
获取WindowContainerToken对应的Change,将Change的成员变量mWindowingMode赋值为传入的windowingMode。
设置容器焦点
/**
* 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;
}
获取WindowContainerToken对应的Change,将Change的成员变量mFocusable赋值为传入的focusable。
HierarchyOp
用来保存容器层级的操作,之后通过mHierarchyOps取出。
HierarchyOp的定义
代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java
/**
* Holds information about a reparent/reorder operation in the hierarchy. This is separate from
* Changes because they must be executed in the same order that they are added.
* @hide
*/
public static final class HierarchyOp implements Parcelable {
//这些TYPE对应着调用的方法
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_PENDING_INTENT = 7;
public static final int HIERARCHY_OP_TYPE_START_SHORTCUT = 8;
public static final int HIERARCHY_OP_TYPE_RESTORE_TRANSIENT_ORDER = 9;
public static final int HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER = 10;
public static final int HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER = 11;
public static final int HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP = 12;
public static final int HIERARCHY_OP_TYPE_REMOVE_TASK = 13;
public static final int HIERARCHY_OP_TYPE_FINISH_ACTIVITY = 14;
public static final int HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS = 15;
public static final int HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH = 16;
public static final int HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION = 17;
// The following key(s) are for use with mLaunchOptions:
// When launching a task (eg. from recents), this is the taskId to be launched.
public static final String LAUNCH_KEY_TASK_ID = "android:transaction.hop.taskId";
// When starting from a shortcut, this contains the calling package.
public static final String LAUNCH_KEY_SHORTCUT_CALLING_PACKAGE =
"android:transaction.hop.shortcut_calling_package";
private final int mType;
// Container we are performing the operation on.
@Nullable
private IBinder mContainer;
// If this is same as mContainer, then only change position, don't reparent.
@Nullable
private IBinder mReparent;
// Moves/reparents to top of parent when {@code true}, otherwise moves/reparents to bottom.
private boolean mToTop;
@Nullable
private Bundle mLaunchOptions;
......
public static final Creator<HierarchyOp> CREATOR = new Creator<HierarchyOp>() {
@Override
public HierarchyOp createFromParcel(Parcel in) {
return new HierarchyOp(in);
}
@Override
public HierarchyOp[] newArray(int size) {
return new HierarchyOp[size];
}
};
private static class Builder {
private final int mType;
@Nullable
private IBinder mContainer;
@Nullable
private IBinder mReparent;
private boolean mToTop;
@Nullable
private Bundle mLaunchOptions;
....
//builder时传递TYPE值,为后续服务端根据不同的TYPE操作做准备
Builder(int type) {
mType = type;
}
......
//把HierarchyOp.Builder中成员变量的值,赋值给HierarchyOp中对应的成员变量
HierarchyOp build() {
final HierarchyOp hierarchyOp = new HierarchyOp(mType);
hierarchyOp.mContainer = mContainer;
hierarchyOp.mReparent = mReparent;
......
}
}
在HierarchyOp中定义了很多常量,如:HIERARCHY_OP_TYPE_REPARENT,这些常量对应构建时传递的类型。
通过HierarchyOp.Builder中构建层级结构,最后在通过其build方法把HierarchyOp.Builder中成员变量的值,赋值给HierarchyOp中对应的成员变量。
HierarchyOp和其内部类HierarchyOp.Builder参数含义相同,关键参数说明:
mType:表示操作类型,其值为通过Builder(int type)中的type传递过来的HierarchyOp中的常量。WindowOrganizerController中的applyHierarchyOp方法也是根据这个mType的值选择不同的操作。
mContainer:表示当前容器的WindowContainerToken。
mReparent:表示父容器的WindowContainerToken。
mToTop:表示reparent/reorder操作后是否需要将子容器移动到父容器之上。true:表示移动到父容器上方,false表示移动到父容器下方。
mLaunchOptions:启动操作项,如分屏时会调用addActivityOptions设置操作项。
startTask
启动task 代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java
/**
* Starts a task by id. The task is expected to already exist (eg. as a recent task).
* @param taskId Id of task to start.
* @param options bundle containing ActivityOptions for the task's top activity.
* @hide
*/
@NonNull
public WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {
mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));
return this;
}
startTask的作用就是通过taskId来启动一个task,这个task是已经启动过的,比如最近任务中的task。
这个里面传递的options,存放了一些启动操作项。例如,分屏操作中调用的addActivityOptions(options1, mSideStage);,就是在options1中存放了关联SideStage的WindowContainerToken与ActivityOptions的KEY_LAUNCH_ROOT_TASK_TOKEN的映射关系。
调用HierarchyOp.createForTaskLaunch构建对应的层级结构HierarchyOp,并将该其添加到mHierarchyOps集合中。
/**
* Holds information about a reparent/reorder operation in the hierarchy. This is separate from
* Changes because they must be executed in the same order that they are added.
* @hide
*/
public static final class HierarchyOp implements Parcelable {
......
/** Create a hierarchy op for launching a task. */
public static HierarchyOp createForTaskLaunch(int taskId, @Nullable Bundle options) {
final Bundle fullOptions = options == null ? new Bundle() : options;
//绑定LAUNCH_KEY_TASK_ID和taskId
fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK)
.setToTop(true)
.setLaunchOptions(fullOptions)
.build();
}
fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);绑定了LAUNCH_KEY_TASK_ID和taskId,后面直接通过LAUNCH_KEY_TASK_ID取出。
这里HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK)中的参数为HIERARCHY_OP_TYPE_LAUNCH_TASK,即HierarchyOp的mType的值为HIERARCHY_OP_TYPE_LAUNCH_TASK
setToTop(true),设置HierarchyOp中mToTop为true;
setLaunchOptions(fullOptions),设置HierarchyOp中mLaunchOptions为fullOptions;
最后通过HierarchyOp.Builder的build()方法构建层级结构HierarchyOp。
待事务提交后,在WindowOrganizerController侧会取出mHierarchyOps中,用来处理需要启动的task和需要设置的options。
removeTask
移除task 代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java
/**
* Finds and removes a task and its children using its container token. The task is removed
* from recents.
* @param containerToken ContainerToken of Task to be removed
*/
@NonNull
public WindowContainerTransaction removeTask(@NonNull WindowContainerToken containerToken) {
mHierarchyOps.add(HierarchyOp.createForRemoveTask(containerToken.asBinder()));
return this;
}
removeTask作用就是根据的task的WindowContainerToken移除task。
参数传入需要移除的task的WindowContainerToken进行移除,同样调用HierarchyOp.createForRemoveTask,并将该层级结构HierarchyOp添加到mHierarchyOps中。
/**
* Holds information about a reparent/reorder operation in the hierarchy. This is separate from
* Changes because they must be executed in the same order that they are added.
* @hide
*/
public static final class HierarchyOp implements Parcelable {
......
/** create a hierarchy op for deleting a task **/
public static HierarchyOp createForRemoveTask(@NonNull IBinder container) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK)
.setContainer(container)
.build();
}
这里HierarchyOp.Builder(HIERARCHY_OP_TYPE_REMOVE_TASK)中的参数为HIERARCHY_OP_TYPE_REMOVE_TASK,即mType的值为HIERARCHY_OP_TYPE_REMOVE_TASK。
setContainer(container),需要移除的task的WindowContainerToken赋值给mContainer。
reorder
Task重排序
/**
* Reorders a container within its parent.
*
* @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
* the bottom.
*/
@NonNull
public WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) {
mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop));
return this;
}
reorder操作是对当前容器以及其父容器的顺序进行调整,这里传入的就是当前需要调整顺序的容器。
调用HierarchyOp.createForReorder,并将该层级结构HierarchyOp添加到mHierarchyOps中。
/**
* Holds information about a reparent/reorder operation in the hierarchy. This is separate from
* Changes because they must be executed in the same order that they are added.
* @hide
*/
public static final class HierarchyOp implements Parcelable {
......
public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER)
.setContainer(container)
.setReparentContainer(container)
.setToTop(toTop)
.build();
}
设置了HierarchyOp的mType的值为HIERARCHY_OP_TYPE_REORDER
这里我们可以看到setContainer(container)和setReparentContainer(container),表示把HierarchyOp中mContainer和mReparent均设置为container,即相同的WindowContainerToken对象。
reorder操作作用是根据mToTop的值调整container在当前父容器中的位置,true:表示移动到父容器上方,false表示移动到父容器下方。
比如:当前container为Task,其父容器则为TaskDisplayArea,TaskDisplayArea中有很多Task,如果其中某个Task需要进行reorder操作,且mToTop的值为true,那么就要把TaskDisplayArea中的该Task移动至所有Task的最上方。
其实现原理可通过后续WindowOrganizerController侧流程了解到。
reparent
Task重定层级 代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java
/**
* 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;
}
reparent操作就是用来调整容器父子层级关系,这里传入当前容器的WindowContainerToken,以及新父容器的WindowContainerToken。
调用HierarchyOp.createForReparent,并将该层级结构HierarchyOp添加到mHierarchyOps中。
/**
* Holds information about a reparent/reorder operation in the hierarchy. This is separate from
* Changes because they must be executed in the same order that they are added.
* @hide
*/
public static final class HierarchyOp implements Parcelable {
......
public static HierarchyOp createForReparent(
@NonNull IBinder container, @Nullable IBinder reparent, boolean toTop) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REPARENT)
.setContainer(container)
.setReparentContainer(reparent)
.setToTop(toTop)
.build();
}
设置了HierarchyOp的mType的值为HIERARCHY_OP_TYPE_REPARENT。
和reorder同理,设置了HierarchyOp中对应的参数,不同点在于根据传递过来的reparent的值,会有几种不同的情况:
reparent不为空,且不等于container,将子容器移入父容器中。reparent不为空,且等于container,那么此次操作不是reparent,而是等效于reorder,根据mToTop的值调整container在当前父容器中的位置。reparent为空,那么将container移入当前Display中。
其实现原理可通过后续WindowOrganizerController侧流程了解到。
reparentTasks
Tasks父级变更 代码路径:frameworks/base/core/java/android/window/WindowContainerTransaction.java
/**
* Reparent's all children tasks of {@param currentParent} in the specified
* {@param windowingMode} and {@param activityType} to {@param newParent} in their current
* z-order.
*
* @param currentParent of the tasks to perform the operation no.
* {@code null} will perform the operation on the display.
* @param newParent for the tasks. {@code null} will perform the operation on the display.
* @param windowingModes of the tasks to reparent. {@code null} ignore this attribute when
* perform the operation.
* @param activityTypes of the tasks to reparent. {@code null} ignore this attribute when
* perform the operation.
* @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
* the bottom.
*/
@NonNull
public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent,
@Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
@Nullable int[] activityTypes, boolean onTop) {
return reparentTasks(currentParent, newParent, windowingModes, activityTypes, onTop,
false /* reparentTopOnly */);
}
/**
* Reparent's all children tasks or the top task of {@param currentParent} in the specified
* {@param windowingMode} and {@param activityType} to {@param newParent} in their current
* z-order.
*
* @param currentParent of the tasks to perform the operation no.
* {@code null} will perform the operation on the display.
* @param newParent for the tasks. {@code null} will perform the operation on the display.
* @param windowingModes of the tasks to reparent.
* @param activityTypes of the tasks to reparent.
* @param onTop When {@code true}, the child goes to the top of parent; otherwise it goes to
* the bottom.
* @param reparentTopOnly When {@code true}, only reparent the top task which fit windowingModes
* and activityTypes.
* @hide
*/
@NonNull
public WindowContainerTransaction reparentTasks(@Nullable WindowContainerToken currentParent,
@Nullable WindowContainerToken newParent, @Nullable int[] windowingModes,
@Nullable int[] activityTypes, boolean onTop, boolean reparentTopOnly) {
mHierarchyOps.add(HierarchyOp.createForChildrenTasksReparent(
currentParent != null ? currentParent.asBinder() : null,
newParent != null ? newParent.asBinder() : null,
windowingModes,
activityTypes,
onTop,
reparentTopOnly));
return this;
}
reparentTasks操作就是把currentParent中的所有子容器,全部reparent到newParent下,即交换父亲。
调用HierarchyOp.createForChildrenTasksReparent,并将该层级结构HierarchyOp添加到mHierarchyOps中。
/**
* Holds information about a reparent/reorder operation in the hierarchy. This is separate from
* Changes because they must be executed in the same order that they are added.
* @hide
*/
public static final class HierarchyOp implements Parcelable {
......
public static HierarchyOp createForChildrenTasksReparent(IBinder currentParent,
IBinder newParent, int[] windowingModes, int[] activityTypes, boolean onTop,
boolean reparentTopOnly) {
return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT)
.setContainer(currentParent)
.setReparentContainer(newParent)
.setWindowingModes(windowingModes)
.setActivityTypes(activityTypes)
.setToTop(onTop)
.setReparentTopOnly(reparentTopOnly)
.build();
}
设置了HierarchyOp的mType的值为HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT,以及其他属性构建到HierarchyOp中。
WindowOrganizerController侧Change处理
回到WindowOrganizerController.applyTransaction中
int containerEffect = applyWindowContainerChange(wc, entry.getValue(),
t.getErrorCallbackToken());
entry的key和value其实就是,容器的token以及其对应的Change。
这里传递的entry.getValue()就是容器对应的Change,继续看applyWindowContainerChange方法。
private int applyWindowContainerChange(WindowContainer wc,
WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) {
//判断容器类型
sanitizeWindowContainer(wc);
//根据不同的容器类型进行不同的操作
if (wc.asDisplayArea() != null) {
//当前容器为DisplayArea
return applyDisplayAreaChanges(wc.asDisplayArea(), c);
} else if (wc.asTask() != null) {
//当前容器为Task
return applyTaskChanges(wc.asTask(), c);
} else if (wc.asTaskFragment() != null && wc.asTaskFragment().isEmbedded()) {
//当前容器为TaskFragment并且是嵌入式的(嵌入到某个Task中)。
return applyTaskFragmentChanges(wc.asTaskFragment(), c, errorCallbackToken);
} else {
//其他情况,直接提交Changes
return applyChanges(wc, c);
}
}
//判断容器类型是否是TaskFragment或者DisplayArea,否则抛出异常
//注:Task类和TaskFragment都算做Task
private void sanitizeWindowContainer(WindowContainer wc) {
if (!(wc instanceof TaskFragment) && !(wc instanceof DisplayArea)) {
throw new RuntimeException("Invalid token in task fragment or displayArea transaction");
}
}
这个方法进一步说明了当前WindowContainerTransaction只支持对Task和DisplayArea进行修改,如果对ActivityRecord,WindowState之类的修改则会报错。
applyDisplayAreaChanges
private int applyDisplayAreaChanges(DisplayArea displayArea,
WindowContainerTransaction.Change c) {
final int[] effects = new int[1];
//提交Configuration相关Changes
effects[0] = applyChanges(displayArea, c);
//c.getChangeMask()获取mChangeMask的值
//CHANGE_IGNORE_ORIENTATION_REQUEST,判断Change中是否有调用过setIgnoreOrientationRequest()
if ((c.getChangeMask()
& WindowContainerTransaction.Change.CHANGE_IGNORE_ORIENTATION_REQUEST) != 0) {
//调用displayArea的setIgnoreOrientationRequest方法
//设置忽略来自app的固定方向请求
if (displayArea.setIgnoreOrientationRequest(c.getIgnoreOrientationRequest())) {
effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
}
}
//遍历当前displayArea下的所有task
displayArea.forAllTasks(task -> {
Task tr = (Task) task;
//c.getChangeMask()获取mChangeMask的值
//这里通过CHANGE_HIDDEN标记判断当前Change中是否有调用过setHidden(隐藏当前容器的方法)
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
//通过task调用setForceHidden方法强制隐藏task
if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
effects[0] |= TRANSACT_EFFECTS_LIFECYCLE;
}
}
});
return effects[0];
}
这个方法主要做了三件事:
- 通过
applyChanges方法提交Configuration相关Changes。 - 通过
CHANGE_IGNORE_ORIENTATION_REQUEST判断在WindowContainerTransaction侧displayArea是否调用过setIgnoreOrientationRequest方法,如果有则通过displayArea调用setIgnoreOrientationRequest方法设置忽略来自app的固定方向请求。 - 遍历displayArea所有task,通过
CHANGE_HIDDEN判断这些task在WindowContainerTransaction侧是否调用过setHidden方法,如果有则通过task调用setForceHidden方法强制隐藏task。
最后返回对应的effects值。
applyTaskFragmentChanges
private int applyTaskFragmentChanges(@NonNull TaskFragment taskFragment,
@NonNull WindowContainerTransaction.Change c, @Nullable IBinder errorCallbackToken) {
//判断PIP中嵌入式TaskFragment是否是画中画模式
if (taskFragment.isEmbeddedTaskFragmentInPip()) {
// No override from organizer for embedded TaskFragment in a PIP Task.
//返回TRANSACT_EFFECTS_NONE不做修改
return TRANSACT_EFFECTS_NONE;
}
// When the TaskFragment is resized, we may want to create a change transition for it, for
// which we want to defer the surface update until we determine whether or not to start
// change transition.
//获取当前taskFragment的bounds,设置到临时变量中
mTmpBounds0.set(taskFragment.getBounds());
//获取嵌入式taskFragment相对于父Task的bounds,设置到临时变量中
mTmpBounds1.set(taskFragment.getRelativeEmbeddedBounds());
//延迟更新Surface
taskFragment.deferOrganizedTaskFragmentSurfaceUpdate();
//获取存储在WindowContainerTransaction.Change中的bounds
final Rect relBounds = c.getRelativeBounds();
if (relBounds != null) {
// Make sure the requested bounds satisfied the min dimensions requirement.
adjustTaskFragmentRelativeBoundsForMinDimensionsIfNeeded(taskFragment, relBounds,
errorCallbackToken);
// For embedded TaskFragment, the organizer set the bounds in parent coordinate to
// prevent flicker in case there is a racing condition between the parent bounds changed
// and the organizer request.
//获取taskFragment父容器的bounds
final Rect parentBounds = taskFragment.getParent().getBounds();
// Convert relative bounds to screen space.
//从相对的bounds转换为绝对bounds
final Rect absBounds = taskFragment.translateRelativeBoundsToAbsoluteBounds(relBounds,
parentBounds);
//设置绝对bounds
c.getConfiguration().windowConfiguration.setBounds(absBounds);
//给嵌入式taskFragment设置相对bounds
taskFragment.setRelativeEmbeddedBounds(relBounds);
}
//提交Configuration相关Changes
final int effects = applyChanges(taskFragment, c);
if (taskFragment.shouldStartChangeTransition(mTmpBounds0, mTmpBounds1)) {
taskFragment.initializeChangeTransition(mTmpBounds0);
}
//更新Surface
taskFragment.continueOrganizedTaskFragmentSurfaceUpdate();
return effects;
}
EmbeddedTaskFragment:嵌入式taskFragment,指的是taskFragment是否嵌入在一个Task之中。
这个方法主要做了三件事:
- 判断画中画模式并处理
- 设置taskFragment相关bounds
- 通过
applyChanges方法提交Configuration相关Changes,并更新surface
最后返回对应的effects值。
applyTaskChanges
private int applyTaskChanges(Task tr, WindowContainerTransaction.Change c) {
//提交Configuration相关Changes
int effects = applyChanges(tr, c);
final SurfaceControl.Transaction t = c.getBoundsChangeTransaction();
//c.getChangeMask()获取mChangeMask的值
//这里通过CHANGE_HIDDEN标记判断当前Change中是否有调用过setHidden(隐藏当前容器的方法)
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_HIDDEN) != 0) {
//通过task调用setForceHidden方法强制隐藏task
if (tr.setForceHidden(FLAG_FORCE_HIDDEN_FOR_TASK_ORG, c.getHidden())) {
effects = TRANSACT_EFFECTS_LIFECYCLE;
}
}
//c.getChangeMask()获取mChangeMask的值
//这里通过CHANGE_FORCE_TRANSLUCENT标记判断当前Change中是否有调用过setForceTranslucent(设置当前容器半透明的方法)
if ((c.getChangeMask()
& WindowContainerTransaction.Change.CHANGE_FORCE_TRANSLUCENT) != 0) {
//通过task调用setForceTranslucent方法强制半透明task
tr.setForceTranslucent(c.getForceTranslucent());
effects = TRANSACT_EFFECTS_LIFECYCLE;
}
//c.getChangeMask()获取mChangeMask的值
//这里通过CHANGE_DRAG_RESIZING标记判断当前Change中是否有调用过setDragResizing(设置当前容器拖动大小的方法)
if ((c.getChangeMask() & WindowContainerTransaction.Change.CHANGE_DRAG_RESIZING) != 0) {
//设置DragResizing
tr.setDragResizing(c.getDragResizing());
}
//获取Change中保存的activity的窗口模式
final int childWindowingMode = c.getActivityWindowingMode();
if (childWindowingMode > -1) {
//遍历Task下的所有activity,设置其窗口模式为前面获取Change中保存的activity的窗口模式
tr.forAllActivities(a -> { a.setWindowingMode(childWindowingMode); });
}
//调整Surface
if (t != null) {
tr.setMainWindowSizeChangeTransaction(t);
}
//设置PIP相关bounds
Rect enterPipBounds = c.getEnterPipBounds();
if (enterPipBounds != null) {
tr.mDisplayContent.mPinnedTaskController.setEnterPipBounds(enterPipBounds);
}
if (c.getWindowingMode() == WindowConfiguration.WINDOWING_MODE_PINNED
&& !tr.inPinnedWindowingMode()) {
final ActivityRecord activity = tr.getTopNonFinishingActivity();
if (activity != null) {
final boolean lastSupportsEnterPipOnTaskSwitch =
activity.supportsEnterPipOnTaskSwitch;
// Temporarily force enable enter PIP on task switch so that PIP is requested
// regardless of whether the activity is resumed or paused.
activity.supportsEnterPipOnTaskSwitch = true;
boolean canEnterPip = activity.checkEnterPictureInPictureState(
"applyTaskChanges", true /* beforeStopping */);
if (canEnterPip) {
canEnterPip = mService.mActivityClientController
.requestPictureInPictureMode(activity);
}
if (!canEnterPip) {
// Restore the flag to its previous state when the activity cannot enter PIP.
activity.supportsEnterPipOnTaskSwitch = lastSupportsEnterPipOnTaskSwitch;
}
}
}
return effects;
}
这个方法主要做了四件事:
- 通过
applyChanges方法提交Configuration相关Changes。 - 通过
c.getChangeMask()判断在WindowContainerTransaction侧Task是否调用过哪些方法,如果有则通过Task调用相关方法进行设置。 - 设置task下所有activity窗口模式,并更新surface。
- 判断画中画模式并处理
最后返回对应的effects值。
applyChanges
private int applyChanges(@NonNull WindowContainer<?> container,
@NonNull WindowContainerTransaction.Change change) {
// The "client"-facing API should prevent bad changes; however, just in case, sanitize
// masks here.
//判断Change中包含哪些configurations相关修改
final int configMask = change.getConfigSetMask() & CONTROLLABLE_CONFIGS;
//判断Change中包含哪些Window相关修改
final int windowMask = change.getWindowSetMask() & CONTROLLABLE_WINDOW_CONFIGS;
int effects = TRANSACT_EFFECTS_NONE;
//获取窗口模式
final int windowingMode = change.getWindowingMode();
//判断configurations是否有修改
if (configMask != 0) {
//判断windowingMode的有效性,且是否发生改变
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).
//windowingMode有效且发生改变的场景
//请求一个Configuration,用于设置新的Configuration
final Configuration c = container.getRequestedOverrideConfiguration();
//设置相关属性
c.setTo(change.getConfiguration(), configMask, windowMask);
} else {
//windowingMode无效或未发生改变的场景
//请求一个Configuration,用于设置新的Configuration
final Configuration c =
new Configuration(container.getRequestedOverrideConfiguration());
//设置相关属性
c.setTo(change.getConfiguration(), configMask, windowMask);
//更新Configuration
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;
}
}
//change.getChangeMask()获取mChangeMask的值
//这里通过CHANGE_FOCUSABLE标记判断当前Change中是否有调用过setFocusable(设置当前容器焦点的方法)
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)) {
Slog.w(TAG, "Dropping unsupported request to set multi-window windowing mode"
+ " during locked task mode.");
return effects;
}
//判断窗口模式是否为PIP
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.getRequestedOverrideWindowingMode();
//设置新窗口模式
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;
}
这个方法主要就是去修改Change中的保存的configurations修改和焦点的修改,以及windowingMode的修改。 接下来我们补充说明一下这段代码中的一些内容
-
结合代码我们看看
CONTROLLABLE_CONFIGS和CONTROLLABLE_WINDOW_CONFIGS的含义/** * Masks specifying which configurations task-organizers can control. Incoming transactions * will be filtered to only include these. */ static final int CONTROLLABLE_CONFIGS = ActivityInfo.CONFIG_WINDOW_CONFIGURATION | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE | ActivityInfo.CONFIG_SCREEN_SIZE | ActivityInfo.CONFIG_LAYOUT_DIRECTION | ActivityInfo.CONFIG_DENSITY; static final int CONTROLLABLE_WINDOW_CONFIGS = WINDOW_CONFIG_BOUNDS | WindowConfiguration.WINDOW_CONFIG_APP_BOUNDS;CONTROLLABLE_CONFIGS:包含窗口的configuration、最新屏幕尺寸、屏幕尺寸、layout方向、density。CONTROLLABLE_WINDOW_CONFIGS:包含容器bounds和应用bounds。 -
关于更新configuration时,是否windowingMode是否有效且发生过修改的代码差异
if (configMask != 0) { if (windowingMode > -1 && windowingMode != container.getWindowingMode()) { //windowingMode有效且发生改变的场景 final Configuration c = container.getRequestedOverrideConfiguration(); c.setTo(change.getConfiguration(), configMask, windowMask); } else { //windowingMode无效或未发生改变的场景 final Configuration c = new Configuration(container.getRequestedOverrideConfiguration()); c.setTo(change.getConfiguration(), configMask, windowMask); container.onRequestedOverrideConfigurationChanged(c); } ...... }这里我们可以看到在到,在windowingMode无效或未发生改变的场景时,通过调用
onRequestedOverrideConfigurationChanged方法进行了更新configuration的操作,然而在windowingMode有效且发生改变的场景却没有更新,这是为什么呢? 这是因为这里还没有对windowingMode进行更新,后续在调用container.setWindowingMode(windowingMode);时,这个方法里面就会去调用onRequestedOverrideConfigurationChanged方法进行了更新configuration相关操作。 调用onRequestedOverrideConfigurationChanged就会触发ConfigurationContainer中onConfigurationChanged,这也是为了避免触发多次ConfigurationContainer中onConfigurationChanged。 因此windowingMode有效且发生改变的场景无需重复调用onRequestedOverrideConfigurationChanged
WindowOrganizerController侧Task相关操作处理
回到WindowOrganizerController.applyTransaction中
effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,
isInLockTaskMode, caller, t.getErrorCallbackToken(),
t.getTaskFragmentOrganizer(), finishTransition);
该方法中applyHierarchyOp方法对象task操作进行相关处理,applyHierarchyOp方法中对task有很多种不同的操作,这里我们主要来看Task的启动、移除、重排序和重定向。
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) {
//获取HierarchyOp.mType值
final int type = hop.getType();
switch (type) {
......
从这里我们可以看到,根据不同的type值进入不同的流程,hop.getType()就是获取的WindowContainerTransaction.HierarchyOp.mType的值,这个值正是WindowContainerTransaction侧调用不同的方法时传递的。
下面结合WindowContainerTransaction侧的构建的层级结构和applyHierarchyOp侧的实现来说明。
startTask
启动Task
WindowContainerTransaction侧调用createForTaskLaunch方法设置HierarchyOp.mType为HIERARCHY_OP_TYPE_LAUNCH_TASK,我们找到applyHierarchyOp中对应的代码。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
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) {
//获取HierarchyOp.mType值
final int type = hop.getType();
switch (type) {
......
case HIERARCHY_OP_TYPE_LAUNCH_TASK: {
mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"launchTask HierarchyOp");
//之前在WindowContainerTransaction.HierarchyOp中
//调用的createForTaskLaunch方法中设置了相应的options,这里是把它取出
final Bundle launchOpts = hop.getLaunchOptions();
//之前在WindowContainerTransaction.HierarchyOp中
//调用的createForTaskLaunch方法中绑定LAUNCH_KEY_TASK_ID和taskId
//这获取的就是需要启动的taskId
final int taskId = launchOpts.getInt(
WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
//获取到应用taskId后从launchOpts中移除该taskId
launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);
//转化成SafeActivityOptions类型
final SafeActivityOptions safeOptions =
SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);
//调用ActivityTaskManagerService侧
//通过ActivityTaskSupervisor.startActivityFromRecents去启动这个Task
//这种启动方式一般是Launcher会用到。
waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents(
caller.mPid, caller.mUid, taskId, safeOptions));
break;
}
......
}
return effects;
}
在分屏流程中,systemui侧会调用addActivityOptions设置操作项,关联了KEY_LAUNCH_ROOT_TASK_TOKEN和stage.mRootTaskInfo.token,之后会通过调用WindowContainerTransaction.startTask方法去设置,这里hop.getLaunchOptions()获取的就是systemui侧设置的mainOptions和sideOptions。
后续真正启动Activity的也就是ActivityTaskSupervisor.startActivityFromRecents方法,这种启动方式一般是Launcher会用到。
removeTask
移除Task
WindowContainerTransaction侧调用createForRemoveTask方法设置HierarchyOp.mType为HIERARCHY_OP_TYPE_REMOVE_TASK,我们找到applyHierarchyOp中对应的代码。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
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) {
//获取HierarchyOp.mType值
final int type = hop.getType();
switch (type) {
case HIERARCHY_OP_TYPE_REMOVE_TASK: {
final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
if (wc == null || wc.asTask() == null || !wc.isAttached()) {
Slog.e(TAG, "Attempt to remove invalid task: " + wc);
break;
}
final Task task = wc.asTask();
task.remove(true, "Applying remove task Hierarchy Op");
break;
}
......
}
return effects;
}
这里我们只需要关注task.remove(true, "Applying remove task Hierarchy Op");也就是调用了Task的remove方法移除Task。
reorder和reparent
Task重排序和重定层级
WindowContainerTransaction侧调用createForReorder和createForReparent方法分别设置HierarchyOp.mType为HIERARCHY_OP_TYPE_REORDER和HIERARCHY_OP_TYPE_REPARENT,我们找到applyHierarchyOp中对应的代码。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
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) {
//获取HierarchyOp.mType值
final int type = hop.getType();
switch (type) {
......
case HIERARCHY_OP_TYPE_REORDER:
case HIERARCHY_OP_TYPE_REPARENT: {
final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
if (wc == null || !wc.isAttached()) {
Slog.e(TAG, "Attempt to operate on detached container: " + wc);
break;
}
// There is no use case to ask the reparent operation in lock-task mode now, so keep
// skipping this operation as usual.
if (isInLockTaskMode && type == HIERARCHY_OP_TYPE_REPARENT) {
Slog.w(TAG, "Skip applying hierarchy operation " + hop
+ " while in lock task mode");
break;
}
if (isLockTaskModeViolation(wc.getParent(), wc.asTask(), isInLockTaskMode)) {
break;
}
if (syncId >= 0) {
addToSyncSet(syncId, wc);
}
if (transition != null) {
transition.collect(wc);
//判断当前操作是否是reparent操作(HIERARCHY_OP_TYPE_REPARENT)
if (hop.isReparent()) {
if (wc.getParent() != null) {
// Collect the current parent. It's visibility may change as
// a result of this reparenting.
transition.collect(wc.getParent());
}
//获取的是WindowContainerTransaction.HierarchyOp中的mReparent
//createForReorder和createForReparent方法中设置了mReparent的值
if (hop.getNewParent() != null) {
//获取父容器
final WindowContainer parentWc =
WindowContainer.fromBinder(hop.getNewParent());
if (parentWc == null) {
Slog.e(TAG, "Can't resolve parent window from token");
break;
}
transition.collect(parentWc);
}
}
}
//sanitizeAndApplyHierarchyOp真正处理reorder和reparent
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
break;
}
......
}
return effects;
}
这个方法兼容reorder和reparent两种操作,唯一不同的是进行reparent操作是会多获取一个父容器,即final WindowContainer parentWc = WindowContainer.fromBinder(hop.getNewParent());,后续通过sanitizeAndApplyHierarchyOp真正处理reorder和reparent
private int sanitizeAndApplyHierarchyOp(WindowContainer container,
WindowContainerTransaction.HierarchyOp hop) {
final Task task = container.asTask();
//task判空处理
if (task == null) {
throw new IllegalArgumentException("Invalid container in hierarchy op");
}
final DisplayContent dc = task.getDisplayContent();
//DisplayContent判空处理
if (dc == null) {
Slog.w(TAG, "Container is no longer attached: " + task);
return TRANSACT_EFFECTS_NONE;
}
final Task as = task;
//目前只有两种操作,reorder和reparent
if (hop.isReparent()) {//判断当前操作是否是reparent
//task.isRootTask(),判断当前task是否是rootTask,即没有父亲的task,最顶层的task。
//task.getParent().asTask().mCreatedByOrganizer,判断父Task是否是由TaskOrganizer.createRootTask创建的,
//其实现在TaskOrganizerController.createRootTask中,这里会把mCreatedByOrganizer设置为true。
final boolean isNonOrganizedRootableTask =
task.isRootTask() || task.getParent().asTask().mCreatedByOrganizer;
//满足上述条件则进行后面reparent的操作
if (isNonOrganizedRootableTask) {
//如果从HierarchyOp中取出mReparent(createForReparent方法中设置了mReparent的值)为null,
//那么将其赋值为默认的TaskDisplayArea,否则就直接获取该parent
WindowContainer newParent = hop.getNewParent() == null
? dc.getDefaultTaskDisplayArea()
: WindowContainer.fromBinder(hop.getNewParent());
//如果最终newParent仍然为空,那么只能中止此次reparent操作。
if (newParent == null) {
Slog.e(TAG, "Can't resolve parent window from token");
return TRANSACT_EFFECTS_NONE;
}
if (task.getParent() != newParent) {//如果当前Task的父容器与newParent不相同
//判断newParent如果是TaskDisplayArea,那么进行相应的reparent的操作
if (newParent.asTaskDisplayArea() != null) {
// For now, reparenting to displayarea is different from other reparents...
//调用task的reparent方法,为task设置新父容器为TaskDisplayArea
as.reparent(newParent.asTaskDisplayArea(), hop.getToTop());
} else if (newParent.asTask() != null) {//判断newParent如果是task
//判断当前是多窗口模式且没有儿子的task
if (newParent.inMultiWindowMode() && task.isLeafTask()) {
//判断newParent是否为画中画
if (newParent.inPinnedWindowingMode()) {
//不允许把Task移动到PIP模式的Task中
Slog.w(TAG, "Can't support moving a task to another PIP window..."
+ " newParent=" + newParent + " task=" + task);
return TRANSACT_EFFECTS_NONE;
}
//判断当前task是否不支持多窗口
if (!task.supportsMultiWindowInDisplayArea(
newParent.asTask().getDisplayArea())) {
//不允许把不支持多窗口的Task移动到分屏rootTask中
Slog.w(TAG, "Can't support task that doesn't support multi-window"
+ " mode in multi-window mode... newParent=" + newParent
+ " task=" + task);
return TRANSACT_EFFECTS_NONE;
}
}
//调用task的reparent方法,为task设置新父容器为newParent,
//根据hop.getToTop()的值判断是放在顶部还是底部
task.reparent((Task) newParent,
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
false /*moveParents*/, "sanitizeAndApplyHierarchyOp");
} else {
throw new RuntimeException("Can only reparent task to another task or"
+ " taskDisplayArea, but not " + newParent);
}
} else {//如果当前Task的父容器与newParent相同,此时的操作则为reorder
//仅调整的是当前Task的rootTask在与其对应的TaskDisplayArea中的位置。
final Task rootTask = (Task) (
(newParent != null && !(newParent instanceof TaskDisplayArea))
? newParent : task.getRootTask());
as.getDisplayArea().positionChildAt(
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM, rootTask,
false /* includingParents */);
}
} else {
throw new RuntimeException("Reparenting leaf Tasks is not supported now. " + task);
}
} else {//如果当前操作是reroder
//调用Task父容器的positionChildAt方法调整当前Task在父容器中的位置即可。
task.getParent().positionChildAt(
hop.getToTop() ? POSITION_TOP : POSITION_BOTTOM,
task, false /* includingParents */);
}
return TRANSACT_EFFECTS_LIFECYCLE;
}
这个方法分别对reparent和reorder操作进行了不同的判断处理,这里总结一下上述方法
根据hop.isReparent()判断,当前操作是reparent操作还是reorder操作
reparent
可以进行reparent操作的两种情况:
-
task.isRootTask(),判断当前task是否是rootTask,即没有父亲的task,最顶层的task。 例如在分屏状态下,Task=512和Task=500的rootTask就是下图中的task=3 -
task.getParent().asTask().mCreatedByOrganizer,判断父Task是否是由TaskOrganizer.createRootTask创建的。
满足进行reparent操作条件后的两种处理方式:
- Task的父容器与newParent不相同
- 判断newParent如果是TaskDisplayArea,那么进行相应的reparent的操作。
- newParent如果不是TaskDisplayArea,判断多窗口相关的条件,之后通过调用task的reparent方法,为task设置新父容器为newParent。
- Task的父容器与newParent相同,即reorder操作 仅调整的是当前Task的rootTask在与其对应的TaskDisplayArea中的位置。
reorder 直接调用Task父容器的positionChildAt方法调整当前Task在父容器中的位置即可。
reparentTasks
Tasks父级变更
WindowContainerTransaction侧调用createForTaskLaunch方法设置HierarchyOp.mType为HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT,我们找到applyHierarchyOp中对应的代码。
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
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) {
//获取HierarchyOp.mType值
final int type = hop.getType();
switch (type) {
......
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: {
effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId,
isInLockTaskMode);
break;
}
......
}
return effects;
}
仅调用了一个reparentChildrenTasksHierarchyOp方法处理,分屏退出流程中会最终会通过该流程处理Task。
private int reparentChildrenTasksHierarchyOp(WindowContainerTransaction.HierarchyOp hop,
@Nullable Transition transition, int syncId, boolean isInLockTaskMode) {
//从HierarchyOp取出currentParent和newParent
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为null,那么newParent赋值为TaskDisplayArea
currentParent = newParent.asTask().getDisplayContent().getDefaultTaskDisplayArea();
} else if (newParent == null) {//如果newParent为null,那么newParent赋值为TaskDisplayArea
newParent = currentParent.asTask().getDisplayContent().getDefaultTaskDisplayArea();
}
if (currentParent == newParent) {
Slog.e(TAG, "reparentChildrenTasksHierarchyOp parent not changing: " + hop);
return TRANSACT_EFFECTS_NONE;
}
if (!currentParent.isAttached()) {
Slog.e(TAG, "reparentChildrenTasksHierarchyOp currentParent detached="
+ currentParent + " hop=" + hop);
return TRANSACT_EFFECTS_NONE;
}
if (!newParent.isAttached()) {
Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent detached="
+ newParent + " hop=" + hop);
return TRANSACT_EFFECTS_NONE;
}
if (newParent.inPinnedWindowingMode()) {
Slog.e(TAG, "reparentChildrenTasksHierarchyOp newParent in PIP="
+ newParent + " hop=" + hop);
return TRANSACT_EFFECTS_NONE;
}
final boolean newParentInMultiWindow = newParent.inMultiWindowMode();
final TaskDisplayArea newParentTda = newParent.asTask() != null
? newParent.asTask().getDisplayArea()
: newParent.asTaskDisplayArea();
final WindowContainer finalCurrentParent = currentParent;
final WindowContainer finalNewParent = newParent;
// We want to collect the tasks first before re-parenting to avoid array shifting on us.
//筛选出currentParent中所有可以进行reparent操作的task
//并将其添加到tasksToReparent临时list中
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()) {
//保存需要放到最前台的task,下标为0
tasksToReparent.add(0, task);
} else {
//不需要放到前台的task正常添加到tasksToReparent集合中即可
tasksToReparent.add(task);
}
return hop.getReparentTopOnly() && tasksToReparent.size() == 1;
});
//从tasksToReparent 这个list中取出临时存储Task
//把这个些task reparent到newParent下
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;
}
这个方法主要就是做了三件事:
- 从HierarchyOp取出
currentParent和newParent,并做判空处理。 - 从
currentParent中筛选出所有可以进行reparent操作的Task,全部添加到一个临时变量tasksToReparent中。 - 然后遍历
tasksToReparent,将tasksToReparent中所有的Task全部reparent到newParent下。
例如在分屏退出流程中,HierarchyOp中存储的currentParent指的就是上下分屏Task(即SideStage和MainStage,而非应用Task),newParent值为null(判空处理后,值为DefaultTaskDisplayArea)。 这个过程实际上就是把上下分屏Task下挂载的Task移动到DefaultTaskDisplayArea下挂载。
其他补充
ActivityTaskSupervisor.startActivityFromRecents
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
/**
* Start the given task from the recent tasks. Do not hold WM global lock when calling this
* method to avoid potential deadlock or permission deny by UriGrantsManager when resolving
* activity (see {@link ActivityStarter.Request#resolveActivity} and
* {@link com.android.server.am.ContentProviderHelper#checkContentProviderUriPermission}).
*
* @return The result code of starter.
*/
int startActivityFromRecents(int callingPid, int callingUid, int taskId,
SafeActivityOptions options) {
final Task task;
final int taskCallingUid;
final String callingPackage;
final String callingFeatureId;
final Intent intent;
final int userId;
//若为分屏场景,获取addActivityOptions方法中传递进来的options,用来获取上下分屏task
final ActivityOptions activityOptions = options != null
? options.getOptions(this)
: null;
boolean moveHomeTaskForward = true;
synchronized (mService.mGlobalLock) {
......
try {
//若为分屏场景,这里主要通过传递的应用taskId来获取对应的应用Task,
//且把taskId对应的Task进行reparent到新上下分屏Task
task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
......
try {
//把task移动到前台,在里面会对focus和resume进行设置
mService.moveTaskToFrontLocked(null /* appThread */,
null /* callingPackage */, task.mTaskId, 0,
......
}
看看moveTaskToFrontLocked方法的流程
代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,
@Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options) {
......
try {
final Task task = mRootWindowContainer.anyTaskForId(taskId);
......
ActivityOptions realOptions = options != null
? options.getOptions(mTaskSupervisor)
: null;
//找到传递的task,将其移至前台
mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
false /* forceNonResizable */);
} finally {
Binder.restoreCallingIdentity(origId);
}
}
继续跟踪findTaskToMoveToFront方法 代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java
/** This doesn't just find a task, it also moves the task to front. */
void findTaskToMoveToFront(Task task, int flags, ActivityOptions options, String reason,
boolean forceNonResizeable) {
//获取最顶层的task
Task currentRootTask = task.getRootTask();
if (currentRootTask == null) {
Slog.e(TAG, "findTaskToMoveToFront: can't move task="
+ task + " to front. Root task is null");
return;
}
try {
......
final ActivityRecord r = task.getTopNonFinishingActivity();
//moveTaskToFront
currentRootTask.moveTaskToFront(task, false /* noAnimation */, options,
r == null ? null : r.appTimeTracker, reason);
......
if (r != null && (options == null || !options.getDisableStartingWindow())) {
// Use a starting window to reduce the transition latency for reshowing the task.
// Note that with shell transition, this should be executed before requesting
// transition to avoid delaying the starting window.
//显示启动窗口StartingWindow
r.showStartingWindow(true /* taskSwitch */);
}
......
}
moveTaskToFront 代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, String reason) {
moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
}
final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
AppTimeTracker timeTracker, boolean deferResume, String reason) {
......
try {
......
// Set focus to the top running activity of this task and move all its parents to top.
//把task移动到最前
top.moveFocusableActivityToTop(reason);
......
if (!deferResume) {
//进行对应resume操作
mRootWindowContainer.resumeFocusedTasksTopActivities();
}
} finally {
mDisplayContent.continueUpdateImeTarget();
}
}
moveFocusableActivityToTop 代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
/**
* Move activity with its root task to front and make the root task focused.
* @param reason the reason to move to top
* @return {@code true} if the root task is focusable and has been moved to top or the activity
* is not yet resumed while the root task is already on top, {@code false} otherwise.
*/
boolean moveFocusableActivityToTop(String reason) {
......
//获取rootTask
final Task rootTask = getRootTask();
......
//当前task移到最前
rootTask.moveToFront(reason, task);
// Report top activity change to tracking services and WM
if (mRootWindowContainer.getTopResumedActivity() == this) {
//把ActivityRecord开始要变成Resumed状态
mAtmService.setLastResumedActivityUncheckLocked(this, reason);
}
return true;
}