前文
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方法。
如果本次修改没有影响到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方法。
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、TaskFragmet和DisplayArea均拥有RemoteToken。
即Task、TaskFragmet和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中的常量。
mContainer
:表示当前容器的WindowContainerToken。
mReparent
:表示父容器的WindowContainerToken。
mToTop
:表示reparent/reorder操作后是否需要将子容器移动到父容器之上。true
:表示移动到父容器上方,false
表示移动到父容器下方。
mLaunchOptions
:启动操作项,如分屏时会调用addActivityOptions设置操作项。
启动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。
移除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
。
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
表示移动到父容器下方。其实现原理可通过后续WindowOrganizerController侧流程了解到。
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();
}
和reorder同理,设置了HierarchyOp中对应的参数,不同点在于根据传递过来的reparent
的值,会有几种不同的情况:
reparent
不为空,且不等于container
,将子容器移入父容器中。reparent
不为空,且等于container
,那么此次操作不是reparent,而是等效于reorder,根据mToTop
的值调整container
在当前父容器中的位置。reparent
为空,那么将container
移入当前Display
中。
其实现原理可通过后续WindowOrganizerController侧流程了解到。
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侧的实现来说明。
启动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。
移除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重排序和重定层级
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);
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());
}
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);
}
}
}
effects |= sanitizeAndApplyHierarchyOp(wc, hop);
break;
}
......
}
return effects;
}