Android U system_server侧WindowContainerTransaction处理(更新中)

发布于:2025-02-11 ⋅ 阅读:(32) ⋅ 点赞:(0)

前文

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等相关操作指令。

因此从入参我们可以清楚该方法的逻辑走向

  1. 如果mTransitionController.getTransitionPlayer()和transition均为空,则直接调用applyTransaction(t, -1 /* syncId */, null, caller);
  2. 如果transition为空,则创建一个Transition对象,然后通过mTransitionController.startCollectOrQueue调用 applyTransaction(wct, -1 /* syncId */, nextTransition, caller, deferred);
  3. 如果transition.isCollecting()和transition.isForcePlaying()均为false,则直接调用applyTransaction(t, -1 /* syncId */, null, caller);
  4. 如果上述条件均不满足的情况,则直接通过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和uid

        static 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端之间的传输提供了支持。
其最重要的就是ChangeHierarchyOp,这里我们可以看到mChanges集合中IBinderChange是一一对应的。

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等。
mChangeMaskmConfigSetMaskmWindowSetMask均为标记,后续有不同类型的修改都会通过该变量进行标记。

常用方法

这些常用方法都会通过mChangeMaskmConfigSetMaskmWindowSetMask这个几个变量在不同的情况设置标记,后续通过这些标记,就可以判断调用过哪些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中,并且在mConfigSetMaskmWindowSetMask中保存了修改标记。

设置容器窗口模式
    /**
     * 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中mToToptrue
setLaunchOptions(fullOptions),设置HierarchyOp中mLaunchOptionsfullOptions
最后通过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中mContainermReparent均设置为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];
    }

这个方法主要做了三件事:

  1. 通过applyChanges方法提交Configuration相关Changes。
  2. 通过CHANGE_IGNORE_ORIENTATION_REQUEST判断在WindowContainerTransaction侧displayArea是否调用过setIgnoreOrientationRequest方法,如果有则通过displayArea调用setIgnoreOrientationRequest方法设置忽略来自app的固定方向请求。
  3. 遍历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之中。

这个方法主要做了三件事:

  1. 判断画中画模式并处理
  2. 设置taskFragment相关bounds
  3. 通过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;
    }

这个方法主要做了四件事:

  1. 通过applyChanges方法提交Configuration相关Changes。
  2. 通过c.getChangeMask()判断在WindowContainerTransaction侧Task是否调用过哪些方法,如果有则通过Task调用相关方法进行设置。
  3. 设置task下所有activity窗口模式,并更新surface。
  4. 判断画中画模式并处理

最后返回对应的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_CONFIGSCONTROLLABLE_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.mTypeHIERARCHY_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.mTypeHIERARCHY_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.mTypeHIERARCHY_OP_TYPE_REORDERHIERARCHY_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;
    }

网站公告

今日签到

点亮在社区的每一天
去签到