前文
Android U 多任务启动分屏——SystemUI流程
前文说到Transitions的startTransition方法中,通过mOrganizer.startNewTransition(type, wct);
提交WindowContainerTransaction相关事务到system_server侧,继续跟踪其流程。
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)@NonNullpublic 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.Stubimplements BLASTSyncEngine.TransactionReadyListener {
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowOrganizerController.java
@Overridepublic IBinder startNewTransition(int type, @Nullable WindowContainerTransaction t) {return startTransition(type, null /* transitionToken */, t);}
最终调用到WindowOrganizerController的startNewTransition方法,该方法就是调用一个startTransition方法,这个方法中传递了type(值为3)、transitionToken(值为null)以及WindowContainerTransaction对象。
处理动画并提交事务
private IBinder startTransition(@WindowManager.TransitionType int type,@Nullable IBinder transitionToken, @Nullable WindowContainerTransaction t) {//检查MANAGE_ACTIVITY_TASKS权限enforceTaskPermission("startTransition()");final CallerInfo caller = new CallerInfo();final long ident = Binder.clearCallingIdentity();try {synchronized (mGlobalLock) {Transition transition = Transition.fromBinder(transitionToken);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.");}applyTransaction(t, -1 /* syncId */, null, caller);return null;}final WindowContainerTransaction wct =t != null ? t : new WindowContainerTransaction();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) -> {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.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();}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);}}
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);}
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId,@Nullable Transition transition, @NonNull CallerInfo caller) {return applyTransaction(t, syncId, transition, caller, null /* finishTransition */);}
处理事务
/*** @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 {if (transition != null) {transition.applyDisplayChangeIfNeeded();}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;if (wc.asTask() != null && wc.inPinnedWindowingMode()&& entry.getValue().getWindowingMode() == WINDOWING_MODE_UNDEFINED) {// We are in pip and going to undefined. Now search hierarchy ops to determine// whether we are removing pip or expanding pip.for (int i = 0; i < hopSize; ++i) {final WindowContainerTransaction.HierarchyOp hop = hops.get(i);if (hop.getType() != HIERARCHY_OP_TYPE_REORDER) continue;final WindowContainer hopWc = WindowContainer.fromBinder(hop.getContainer());if (!wc.equals(hopWc)) continue;forceHiddenForPip = !hop.getToTop();}}if (forceHiddenForPip) {wc.asTask().setForceHidden(FLAG_FORCE_HIDDEN_FOR_PINNED_TASK, true /* set */);// When removing pip, make sure that onStop is sent to the app ahead of// onPictureInPictureModeChanged.// See also PinnedStackTests#testStopBeforeMultiWindowCallbacksOnDismisswc.asTask().ensureActivitiesVisible(null, 0, PRESERVE_WINDOWS);wc.asTask().mTaskSupervisor.processStoppingAndFinishingActivities(null /* launchedActivity */, false /* processPausingActivities */,"force-stop-on-removing-pip");}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);}}// Hierarchy changesif (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.entries = t.getChanges().entrySet().iterator();while (entries.hasNext()) {final Map.Entry<IBinder, WindowContainerTransaction.Change> entry = entries.next();final WindowContainer wc = WindowContainer.fromBinder(entry.getKey());if (wc == null || !wc.isAttached()) {Slog.e(TAG, "Attempt to operate on detached container: " + wc);continue;}final Task task = wc.asTask();final Rect surfaceBounds = entry.getValue().getBoundsChangeSurfaceBounds();if (task == null || !task.isAttached() || surfaceBounds == null) {continue;}if (!task.isOrganized()) {final Task parent = task.getParent() != null ? task.getParent().asTask() : null;// Also allow direct children of created-by-organizer tasks to be// controlled. In the future, these will become organized anyways.if (parent == null || !parent.mCreatedByOrganizer) {throw new IllegalArgumentException("Can't manipulate non-organized task surface " + task);}}final SurfaceControl.Transaction sft = new SurfaceControl.Transaction();final SurfaceControl sc = task.getSurfaceControl();sft.setPosition(sc, surfaceBounds.left, surfaceBounds.top);if (surfaceBounds.isEmpty()) {sft.setWindowCrop(sc, null);} else {sft.setWindowCrop(sc, surfaceBounds.width(), surfaceBounds.height());}task.setMainWindowSizeChangeTransaction(sft);}if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {mService.mTaskSupervisor.setDeferRootVisibilityUpdate(false /* deferUpdate */);// Already calls ensureActivityConfigmService.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;}
Task事务处理
接WindowOrganizerController.java中
private int applyTransaction(@NonNull WindowContainerTransaction t, int syncId, @Nullable Transition transition, @NonNull CallerInfo caller, @Nullable Transition finishTransition)
Task区域大小bounds变化处理
int containerEffect = applyWindowContainerChange(wc, entry.getValue(),t.getErrorCallbackToken());
Task相关操作处理
该方法中applyHierarchyOp方法对象task操作进行相关处理
effects |= applyHierarchyOp(hops.get(i), effects, syncId, transition,isInLockTaskMode, caller, t.getErrorCallbackToken(),t.getTaskFragmentOrganizer(), finishTransition);
applyHierarchyOp方法中对task有很多种不同的操作,这里我们主要来看Task的启动、移除、重排序和重定向。
下面结合WindowContainerTransaction侧的构建的层级结构和applyHierarchyOp侧的实现来说明。
启动Task
WindowContainerTransaction
代码路径: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*/@NonNullpublic WindowContainerTransaction startTask(int taskId, @Nullable Bundle options) {mHierarchyOps.add(HierarchyOp.createForTaskLaunch(taskId, options));return this;}/*** 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;fullOptions.putInt(LAUNCH_KEY_TASK_ID, taskId);return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_LAUNCH_TASK).setToTop(true).setLaunchOptions(fullOptions).build();}
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) {final int type = hop.getType();switch (type) {......case HIERARCHY_OP_TYPE_LAUNCH_TASK: {mService.mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,"launchTask HierarchyOp");final Bundle launchOpts = hop.getLaunchOptions();final int taskId = launchOpts.getInt(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);launchOpts.remove(WindowContainerTransaction.HierarchyOp.LAUNCH_KEY_TASK_ID);final SafeActivityOptions safeOptions =SafeActivityOptions.fromBundle(launchOpts, caller.mPid, caller.mUid);waitAsyncStart(() -> mService.mTaskSupervisor.startActivityFromRecents(caller.mPid, caller.mUid, taskId, safeOptions));break;}......}return effects;}
移除Task
WindowContainerTransaction
代码路径: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*/@NonNullpublic WindowContainerTransaction removeTask(@NonNull WindowContainerToken containerToken) {mHierarchyOps.add(HierarchyOp.createForRemoveTask(containerToken.asBinder()));return this;}/*** 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();}
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) {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
代码路径: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.*/@NonNullpublic WindowContainerTransaction reparent(@NonNull WindowContainerToken child,@Nullable WindowContainerToken parent, boolean onTop) {mHierarchyOps.add(HierarchyOp.createForReparent(child.asBinder(),parent == null ? null : parent.asBinder(),onTop));return this;}/*** 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.*/@NonNullpublic WindowContainerTransaction reorder(@NonNull WindowContainerToken child, boolean onTop) {mHierarchyOps.add(HierarchyOp.createForReorder(child.asBinder(), onTop));return this;}/*** 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();}public static HierarchyOp createForReorder(@NonNull IBinder container, boolean toTop) {return new HierarchyOp.Builder(HIERARCHY_OP_TYPE_REORDER).setContainer(container).setReparentContainer(container).setToTop(toTop).build();}
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) {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;}