Android U 自由窗口(浮窗)——启动流程(system_server侧流程)

194 阅读12分钟

juejin.cn/post/756606…

system_server侧流程

代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

    public final int startActivityFromRecents(int taskId, Bundle bOptions) {
        mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
                "startActivityFromRecents()");
        //获取
        final int callingPid = Binder.getCallingPid();
        final int callingUid = Binder.getCallingUid();
        //封装options
        final SafeActivityOptions safeOptions = SafeActivityOptions.fromBundle(bOptions);
        final long origId = Binder.clearCallingIdentity();
        try {
            return mTaskSupervisor.startActivityFromRecents(callingPid, callingUid, taskId,
                    safeOptions);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

这里callingPidcallingUid为桌面piduid,后续传递参数继续调用到ActivityTaskSupervisor.startActivityFromRecents。

代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

    /**
     * Start the given task from the recent tasks. Do not hold WM global lock when calling this
     * method to avoid potential deadlock or permission deny by UriGrantsManager when resolving
     * activity (see {@link ActivityStarter.Request#resolveActivity} and
     * {@link com.android.server.am.ContentProviderHelper#checkContentProviderUriPermission}).
     *
     * @return The result code of starter.
     */
    int startActivityFromRecents(int callingPid, int callingUid, int taskId,
            SafeActivityOptions options) {
        final Task task;
        final int taskCallingUid;
        final String callingPackage;
        final String callingFeatureId;
        final Intent intent;
        final int userId;
        //获取传递过来的options
        final ActivityOptions activityOptions = options != null
                ? options.getOptions(this)
                : null;
        boolean moveHomeTaskForward = true;
        synchronized (mService.mGlobalLock) {
            final boolean isCallerRecents = mRecentTasks.isCallerRecents(callingUid);
            int activityType = ACTIVITY_TYPE_UNDEFINED;
            if (activityOptions != null) {
                activityType = activityOptions.getLaunchActivityType();
                //这里主要就是判断是否要 冻结当前最近的任务列表顺序,直到用户与当前应用程序进行交互或发生超时。
                if (activityOptions.freezeRecentTasksReordering() && (isCallerRecents
                        || ActivityTaskManagerService.checkPermission(MANAGE_ACTIVITY_TASKS,
                                callingPid, callingUid) == PERMISSION_GRANTED)) {
                    mRecentTasks.setFreezeTaskListReordering();
                }
                //传递过来options中没有设置过mLaunchRootTask,因此getLaunchRootTask()的值为空
                if (activityOptions.getLaunchRootTask() != null) {
                    // Don't move home activity forward if there is a launch root set.
                    moveHomeTaskForward = false;
                }
            }
            //判断当前activity类型的合法性
            if (activityType == ACTIVITY_TYPE_HOME || activityType == ACTIVITY_TYPE_RECENTS) {
                throw new IllegalArgumentException("startActivityFromRecents: Task "
                        + taskId + " can't be launch in the home/recents root task.");
            }

            boolean shouldStartActivity = false;
            //延迟窗口布局
            mService.deferWindowLayout();
            try {
                //根据taskId获取Task对象
                task = mRootWindowContainer.anyTaskForId(taskId,
                        MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);
                //task为空时,做窗口绘制处理并抛出异常
                if (task == null) {
                    mWindowManager.executeAppTransition();
                    throw new IllegalArgumentException(
                            "startActivityFromRecents: Task " + taskId + " not found.");
                }

                if (moveHomeTaskForward) {
                    // We always want to return to the home activity instead of the recents
                    // activity from whatever is started from the recents activity, so move
                    // the home root task forward.
                    // TODO (b/115289124): Multi-display supports for recents.
                    //moveHomeRootTaskToFront会获取桌面rootTask,然后将其移至最前台
                    mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront(
                            "startActivityFromRecents");
                }

                // If the user must confirm credentials (e.g. when first launching a work
                // app and the Work Challenge is present) let startActivityInPackage handle
                // the intercepting.
                
                //shouldConfirmCredentials,涉及一些安全性场景,不深究
                //task.getRootActivity(),判断当前Task下面最顶端的activity是否存在
                if (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId)
                        && task.getRootActivity() != null) {
                    final ActivityRecord targetActivity = task.getTopNonFinishingActivity();
                    ......
                    try {
                        //这里是把自由窗口的Task移到最顶部前端
                        mService.moveTaskToFrontLocked(null /* appThread */,
                                null /* callingPackage */, task.mTaskId, 0, options);
                        // Apply options to prevent pendingOptions be taken when scheduling
                        // activity lifecycle transaction to make sure the override pending app
                        // transition will be applied immediately.
                        //activityOptions.getAnimationType()没有设置,值为-1,不走该流程
                        if (activityOptions != null
                                && activityOptions.getAnimationType() == ANIM_REMOTE_ANIMATION) {
                            targetActivity.mPendingRemoteAnimation =
                                    activityOptions.getRemoteAnimationAdapter();
                        }
                        targetActivity.applyOptionsAnimation();
                        //activityOptions.getLaunchCookie()没有设置,值为null,不走该流程
                        if (activityOptions != null && activityOptions.getLaunchCookie() != null) {
                            targetActivity.mLaunchCookie = activityOptions.getLaunchCookie();
                        }
                    } finally {
                        mActivityMetricsLogger.notifyActivityLaunched(launchingState,
                                START_TASK_TO_FRONT, false /* newActivityCreated */,
                                targetActivity, activityOptions);
                    }

                    mService.getActivityStartController().postStartActivityProcessingForLastStarter(
                            task.getTopNonFinishingActivity(), ActivityManager.START_TASK_TO_FRONT,
                            task.getRootTask());

                    // As it doesn't go to ActivityStarter.executeRequest() path, we need to resume
                    // app switching here also.
                    mService.resumeAppSwitches();
                    return ActivityManager.START_TASK_TO_FRONT;
                }
                // The task is empty or needs to show the confirmation for credential.
                shouldStartActivity = true;
            } finally {
                if (!shouldStartActivity) {
                    //继续窗口布局,与前面mService.deferWindowLayout()延迟窗口布局相呼应。
                    mService.continueWindowLayout();
                }
            }
            ......
        }
        // ActivityStarter will acquire the lock where the places need, so execute the request
        // outside of the lock.
        try {
            // We need to temporarily disable the explicit intent filter matching enforcement
            // because Task does not store the resolved type of the intent data, causing filter
            // mismatch in certain cases. (b/240373119)
            // 从代码流程上看前面条件 (!mService.mAmInternal.shouldConfirmCredentials(task.mUserId) && task.getRootActivity() != null) 为false时才走后续流程。
           PackageManagerServiceUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(true);
            //通过startActivityInPackage启动相应的Task中对应Activity,传递options、task等关键参数。
            return mService.getActivityStartController().startActivityInPackage(taskCallingUid,
                    callingPid, callingUid, callingPackage, callingFeatureId, intent, null, null,
                    null, 0, 0, options, userId, task, "startActivityFromRecents",
                    false /* validateIncomingUser */, null /* originatingPendingIntent */,
                    BackgroundStartPrivileges.NONE);
        } finally {
            PackageManagerServiceUtils.DISABLE_ENFORCE_INTENTS_TO_MATCH_INTENT_FILTERS.set(false);
            synchronized (mService.mGlobalLock) {
                //继续窗口布局,与前面mService.deferWindowLayout()延迟窗口布局相呼应。
                mService.continueWindowLayout();
            }
        }
    }

startActivityFromRecents方法主要做了三件事:

  1. 使用anyTaskForId方法,通过taskId找到对应的Task并设置其windowingMode。
  2. 通过moveHomeRootTaskToFront方法,移动桌面Task至做前台。
  3. 通过moveTaskToFrontLocked方法,移动需要切换自由窗口Task移至最前台。

通过taskId找到对应的Task并设置其windowingMode

task = mRootWindowContainer.anyTaskForId(taskId,
        MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, activityOptions, ON_TOP);

这里主要传递应用taskId和activityOptions(携带需要设置的自由窗口相关参数)。

RootWindowContainer.anyTaskForId

代码路径:frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

    /**
     * Returns a {@link Task} for the input id if available. {@code null} otherwise.
     *
     * @param id        Id of the task we would like returned.
     * @param matchMode The mode to match the given task id in.
     * @param aOptions  The activity options to use for restoration. Can be null.
     * @param onTop     If the root task for the task should be the topmost on the display.
     */
    Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode,
            @Nullable ActivityOptions aOptions, boolean onTop) {
        // If options are set, ensure that we are attempting to actually restore a task
        if (matchMode != MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE && aOptions != null) {
            throw new IllegalArgumentException("Should not specify activity options for non-restore"
                    + " lookup");
        }

        //简单说就是创建了一个 task -> task.isTaskId(id) 的lamda表达式
        final PooledPredicate p = PooledLambda.obtainPredicate(
                Task::isTaskId, PooledLambda.__(Task.class), id);
        //查找符合条件的task
        Task task = getTask(p);
        //将 PooledPredicate 对象回收到对象池中,以便重用,避免频繁的垃圾回收
        p.recycle();

        if (task != null) {
            if (aOptions != null) {
                // Resolve the root task the task should be placed in now based on options
                // and reparent if needed.
                // TODO(b/229927851) For split-screen, setLaunchRootTask is no longer the "root"
                // task, consider to rename methods like "parentTask" instead of "rootTask".
                //找到rootTask
                final Task targetRootTask =
                        getOrCreateRootTask(null, aOptions, task, onTop);
                // When launch with ActivityOptions#getLaunchRootTask, the "root task" just mean the
                // parent of current launch, not the "root task" in hierarchy.
                //这里是判断当前Task的获取到的rootTask与其task.getRootTask()的场景
                //我们一般启动应用时情况如下:
                //targetRootTask != null,true
                //task.getRootTask() != targetRootTask,false
                //task.getParent() != targetRootTask,true
                //因此我们这里为false
                //这个场景一般是task参数在特殊情况变null时,getOrCreateRootTask中会重新创建Task
                if (targetRootTask != null && task.getRootTask() != targetRootTask
                        && task.getParent() != targetRootTask) {
                    final int reparentMode = onTop
                            ? REPARENT_MOVE_ROOT_TASK_TO_FRONT : REPARENT_LEAVE_ROOT_TASK_IN_PLACE;
                    task.reparent(targetRootTask, onTop, reparentMode, ANIMATE, DEFER_RESUME,
                            "anyTaskForId");
                }
            }
            //返回获取到的task
            return task;
        }

        //task为空的其他情况,省略
        ......
        
        if (DEBUG_RECENTS) Slog.w(TAG_RECENTS, "Restored task id=" + id + " from in recents");
        return task;
    }

这个方法实际上就是通过getOrCreateRootTask做一些操作,主要是给传递的task设置options,并且找到task的rootTask,然后返回给targetRootTask,做后续的条件判断处理,这里最后return的通过getTask(p)获取到的task。

在分析getOrCreateRootTask方法之前,我们先了解下这个条件判断

                if (targetRootTask != null && task.getRootTask() != targetRootTask
                        && task.getParent() != targetRootTask) {

这里面有几个不同的task,我们看看这个task分别代表什么 targetRootTask:判断通过getOrCreateRootTask(null, aOptions, task, onTop);方法获取到的task的rootTask不为null。

task.getRootTask():获取task的rootTask。

task.getParent():获取task的父节点。

一般情况targetRootTasktask.getRootTask()均为获取当前task的rootTask,因此它们相同,即task.getRootTask() != targetRootTask不成立,所以不会进入到后续条件。

我们结合实际的例子来看便于理解,如下图: 在这里插入图片描述 这里我们以需要分屏的应用com.android.messaging为例,其Task=1265,从图中我们可以看出来该应用只有一个Task,所以其Task就是rootTask,targetRootTasktask.getRootTask()也是Task=1265,其task.getParent()就是DefaultTaskDisplayArea

再以com.android.launcher3为例,其Task=1261,从图中我们可以看到有多个task镶嵌,targetRootTasktask.getRootTask()值为最顶部的Task=1task.getParent()即是Task=1261的父节点Task=1,因此这种情况三者全部相等。 注:不熟悉窗口层级结构的朋友可以先看 Android T 窗口层级其一 —— 容器类

这里我们重点分析下getOrCreateRootTask方法。

                final Task targetRootTask =
                        getOrCreateRootTask(null, aOptions, task, onTop);

这里传递了从taskId中找到的对应task,以及前面创建options,其中参数null表示ActivityRecord为空。

RootWindowContainer.getOrCreateRootTask

    /**
     * Returns the right root task to use for launching factoring in all the input parameters.
     *
     * @param r              The activity we are trying to launch. Can be null.
     * @param options        The activity options used to the launch. Can be null.
     * @param candidateTask  The possible task the activity might be launched in. Can be null.
     * @param sourceTask     The task requesting to start activity. Can be null.
     * @param launchParams   The resolved launch params to use.
     * @param launchFlags    The launch flags for this launch.
     * @param realCallingPid The pid from {@link ActivityStarter#setRealCallingPid}
     * @param realCallingUid The uid from {@link ActivityStarter#setRealCallingUid}
     * @return The root task to use for the launch.
     */
    Task getOrCreateRootTask(@Nullable ActivityRecord r,
            @Nullable ActivityOptions options, @Nullable Task candidateTask,
            @Nullable Task sourceTask, boolean onTop,
            @Nullable LaunchParamsController.LaunchParams launchParams, int launchFlags) {
        // First preference goes to the launch root task set in the activity options.
        if (options != null) {
            //这边传递过来的options没有设置过mLaunchRootTask
            //因此options.getLaunchRootTask()为null,因此不进入此流程
            final Task candidateRoot = Task.fromWindowContainerToken(options.getLaunchRootTask());
            if (candidateRoot != null && canLaunchOnDisplay(r, candidateRoot)) {
                return candidateRoot;
            }
        }

        // Next preference goes to the task id set in the activity options.
        if (options != null) {
            //options.getLaunchRootTask()为null,因此不进入此流程
            final int candidateTaskId = options.getLaunchTaskId();
            if (candidateTaskId != INVALID_TASK_ID) {
                ......
            }
        }

        // Next preference goes to the TaskDisplayArea candidate from launchParams
        // or activity options.
        TaskDisplayArea taskDisplayArea = null;
        //launchParams传递过来的值为null
        if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) {
            taskDisplayArea = launchParams.mPreferredTaskDisplayArea;
        } else if (options != null) {//options不为空,进入此流程给taskDisplayArea赋值
            //当前的options中没有设置,因此options.getLaunchTaskDisplayArea()值为空
            //即taskDisplayArea获取的值为null。
            final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
            taskDisplayArea = daToken != null
                    ? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
            if (taskDisplayArea == null) {
                //options.getLaunchDisplayId()为null,因此不进入此流程
                final int launchDisplayId = options.getLaunchDisplayId();
                if (launchDisplayId != INVALID_DISPLAY) {
                    final DisplayContent displayContent = getDisplayContent(launchDisplayId);
                    if (displayContent != null) {
                        taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
                    }
                }
            }
        }

        //获取activityType
        //我们这里r即ActivityRecord为空,因此获取candidateTask的type
        //即activityType为ACTIVITY_TYPE_STANDARD
        final int activityType = resolveActivityType(r, options, candidateTask);
        //此时taskDisplayArea仍然为空
        if (taskDisplayArea != null) {
            if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) {
                return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask,
                        sourceTask, launchParams, launchFlags, activityType, onTop);
            } else {
                taskDisplayArea = null;
            }
        }

        // Give preference to the root task and display of the input task and activity if they
        // match the mode we want to launch into.
        Task rootTask = null;
        //传递过来的candidateTask不为空,因此获取其rootTask
        if (candidateTask != null) {
            rootTask = candidateTask.getRootTask();
        }
        //rootTask为空且ActivityRecord不为空的情况,通过ActivityRecord获取rootTask
        if (rootTask == null && r != null) {
            rootTask = r.getRootTask();
        }
        //launchParams为null,此时windowingMode为WINDOWING_MODE_UNDEFINED
        int windowingMode = launchParams != null ? launchParams.mWindowingMode
                : WindowConfiguration.WINDOWING_MODE_UNDEFINED;
        if (rootTask != null) {
            //获取rootTask对应的taskDisplayArea
            taskDisplayArea = rootTask.getDisplayArea();
            //此时taskDisplayArea不为null
            //canLaunchOnDisplay中传递的r(ActivityRecord)和DisplayContent的DisplayId
            //其中ActivityRecord为空,从canLaunchOnDisplay方法流程中可以得知该情况返回为true
            if (taskDisplayArea != null
                    && canLaunchOnDisplay(r, taskDisplayArea.mDisplayContent.mDisplayId)) {
                //此时windowingMode为WINDOWING_MODE_UNDEFINED
                if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
                    //该方法通过options.getLaunchWindowingMode()获取了自由窗口的windowingMode,其值为WINDOWING_MODE_FREEFORM
                    windowingMode = taskDisplayArea.resolveWindowingMode(r, options, candidateTask);
                }
                // Always allow organized tasks that created by organizer since the activity type
                // of an organized task is decided by the activity type of its top child, which
                // could be incompatible with the given windowing mode and activity type.
                //rootTask.isCompatible(windowingMode, activityType),判断当前应用的windowingMode和activityType
                //和新设置的windowingMode和activityType是否一致,一致则返回true,不一致则返回false
                //简单来说这个方法就是判断windowingMode和activityType是否需要更新,如果需要更新就继续后面的流程。
                //rootTask.mCreatedByOrganizer从代码上看涉及TaskFragment才会有true的情况
                if (rootTask.isCompatible(windowingMode, activityType)
                        || rootTask.mCreatedByOrganizer) {
                    //直接返回当前rootTask
                    return rootTask;
                }
            } else {
                taskDisplayArea = null;
            }

        }

        // Falling back to default task container
        //taskDisplayArea为空时,直接获取默认的taskDisplayArea
        if (taskDisplayArea == null) {
            taskDisplayArea = getDefaultTaskDisplayArea();
        }
        //在taskDisplayArea中调用getOrCreateRootTask
        return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
                launchParams, launchFlags, activityType, onTop);
    }

本次启动自由窗口时,rootTask = candidateTask.getRootTask();不为null,因此进入到rootTask != null的流程。 这里com.android.messaging进入自由窗口为例,使用dump命令adb shell dumpsys activity containers在这里插入图片描述 从dump中可以看到com.android.messaging的task为1226,在其之上没有别的task,因此rootTask实际上就是candidateTask它本身。

rootTask.isCompatible(windowingMode, activityType) || rootTask.mCreatedByOrganizer条件中,返回的是false,因此不会直接返回rootTask。 这里我们主要先讲解下rootTask.isCompatible(windowingMode, activityType) 代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java

    public boolean isCompatible(int windowingMode, int activityType) {
        // TODO: Should we just move this to ConfigurationContainer?
        if (activityType == ACTIVITY_TYPE_UNDEFINED) {
            // Undefined activity types end up in a standard root task once the root task is
            // created on a display, so they should be considered compatible.
            activityType = ACTIVITY_TYPE_STANDARD;
        }
        return super.isCompatible(windowingMode, activityType);
    }

判断activityType类型是否是ACTIVITY_TYPE_UNDEFINED,如果是则改为ACTIVITY_TYPE_STANDARD,之后调用其父类ConfigurationContainer.isCompatible方法 代码路径:frameworks/base/services/core/java/com/android/server/wm/ConfigurationContainer.java

    /**
     * Returns true if this container is compatible with the input windowing mode and activity type.
     * The container is compatible:
     * - If {@param activityType} and {@param windowingMode} match this container activity type and
     * windowing mode.
     * - If {@param activityType} is {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
     * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} and this containers activity type is also
     * standard or undefined and its windowing mode matches {@param windowingMode}.
     * - If {@param activityType} isn't {@link WindowConfiguration#ACTIVITY_TYPE_UNDEFINED} or
     * {@link WindowConfiguration#ACTIVITY_TYPE_STANDARD} or this containers activity type isn't
     * also standard or undefined and its activity type matches {@param activityType} regardless of
     * if {@param windowingMode} matches the containers windowing mode.
     */
    public boolean isCompatible(int windowingMode, int activityType) {
        //获取当前activityType和windowingMode
        final int thisActivityType = getActivityType();
        final int thisWindowingMode = getWindowingMode();
        //比较当前activityType和windowingMode和传递过来的activityType和windowingMode
        final boolean sameActivityType = thisActivityType == activityType;
        final boolean sameWindowingMode = thisWindowingMode == windowingMode;
        
        //activityType和windowingMode均未发生变化
        if (sameActivityType && sameWindowingMode) {
            return true;
        }

        //activityType发生了变化
        if ((activityType != ACTIVITY_TYPE_UNDEFINED && activityType != ACTIVITY_TYPE_STANDARD)
                || !isActivityTypeStandardOrUndefined()) {
            // Only activity type need to match for non-standard activity types that are defined.
            return sameActivityType;
        }

        // Otherwise we are compatible if the windowing mode is the same.
        //windowingMode发生了变化
        return sameWindowingMode;
    }

通过getActivityType()和getWindowingMode()分别获取了当前activityType和windowingMode,然后进行变化,如果两者之一出现变化则返回false。

因此我们这里没有直接返回rootTask,而是调用到了taskDisplayArea.getOrCreateRootTask。

        return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
                launchParams, launchFlags, activityType, onTop);

这里先回顾说明下传递的参数: r:ActivityRecord类型,其值由RootWindowContainer.anyTaskForId中传递过来,值为null

options:ActivityOptions类型,其值从最初桌面侧FreeformSystemShortcut.startActivity调用startActivityFromRecents传递过来,包含来设置 窗口模式(mLaunchWindowingMode)窗口大小(mLaunchBounds) 参数。

candidateTask:Task类型,其值由RootWindowContainer.anyTaskForId中传递过来,值为桌面侧传递过来taskId对应的Task

sourceTask:Task类型,其值由RootWindowContainer.getOrCreateRootTask传递,值为null

activityType:其值由RootWindowContainer.getOrCreateRootTask获取并传递,值为ACTIVITY_TYPE_STANDARD1

launchParams:LaunchParamsController.LaunchParams类型,其值由RootWindowContainer.getOrCreateRootTask传递,值为null

launchFlags:int类型,其值由RootWindowContainer.getOrCreateRootTask传递,值为0

onTop:boolean类型,其值由ActivityTaskSupervisor.startActivityFromRecents的anyTaskForId中传递过来,值为 ActivityTaskSupervisor.ON_TOPtrue

TaskDisplayArea.getOrCreateRootTask

代码路径:frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java

    /**
     * Returns an existing root task compatible with the input params or creates one
     * if a compatible root task doesn't exist.
     *
     * @see #getOrCreateRootTask(int, int, boolean)
     */
    Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
            @Nullable Task candidateTask, @Nullable Task sourceTask,
            @Nullable LaunchParams launchParams, int launchFlags, int activityType, boolean onTop) {
        //设置windowingMode初始值为WINDOWING_MODE_UNDEFINED
        int windowingMode = WINDOWING_MODE_UNDEFINED;
        if (launchParams != null) {
            // If launchParams isn't null, windowing mode is already resolved.
            windowingMode = launchParams.mWindowingMode;
        } else if (options != null) {
            // If launchParams is null and options isn't let's use the windowing mode in the
            // options.
            //这里options不为空,options.getLaunchWindowingMode()值为WINDOWING_MODE_FREEFORM
            windowingMode = options.getLaunchWindowingMode();
        }
        // Validate that our desired windowingMode will work under the current conditions.
        // UNDEFINED windowing mode is a valid result and means that the new root task will inherit
        // it's display's windowing mode.
        //判断windowingMode有效性,并赋值
        windowingMode = validateWindowingMode(windowingMode, r, candidateTask);
        //继续传递参数
        return getOrCreateRootTask(windowingMode, activityType, onTop, candidateTask, sourceTask,
                options, launchFlags);
    }

这里主要就是通过options.getLaunchWindowingMode()获取了windowingMode (WINDOWING_MODE_FREEFORM),在将其传递到getOrCreateRootTask方法中。

    /**
     * When two level tasks are required for given windowing mode and activity type, returns an
     * existing compatible root task or creates a new one.
     * For one level task, the candidate task would be reused to also be the root task or create
     * a new root task if no candidate task.
     *
     * @param windowingMode The windowing mode the root task should be created in.
     * @param activityType  The activityType the root task should be created in.
     * @param onTop         If true the root task will be created at the top of the display,
     *                      else at the bottom.
     * @param candidateTask The possible task the activity might be launched in. Can be null.
     * @param sourceTask    The task requesting to start activity. Used to determine which of the
     *                      adjacent roots should be launch root of the new task. Can be null.
     * @param options       The activity options used to the launch. Can be null.
     * @param launchFlags   The launch flags for this launch.
     * @return The root task to use for the launch.
     * @see #getRootTask(int, int)
     */
    Task getOrCreateRootTask(int windowingMode, int activityType, boolean onTop,
            @Nullable Task candidateTask, @Nullable Task sourceTask,
            @Nullable ActivityOptions options, int launchFlags) {
        //我们这里windowingMode传递的是WINDOWING_MODE_FREEFORM,
        //因此resolvedWindowingMode为WINDOWING_MODE_FREEFORM
        final int resolvedWindowingMode =
                windowingMode == WINDOWING_MODE_UNDEFINED ? getWindowingMode() : windowingMode;
        // Need to pass in a determined windowing mode to see if a new root task should be created,
        // so use its parent's windowing mode if it is undefined.
        //我们这里resolvedWindowingMode是WINDOWING_MODE_FREEFORM
        //activityType是ACTIVITY_TYPE_STANDARD,因此alwaysCreateRootTask为true
        //这取反为false,因此不进入该逻辑
        if (!alwaysCreateRootTask(resolvedWindowingMode, activityType)) {
            Task rootTask = getRootTask(resolvedWindowingMode, activityType);
            if (rootTask != null) {
                return rootTask;
            }
        } else if (candidateTask != null) {
            final int position = onTop ? POSITION_TOP : POSITION_BOTTOM;
            //这里getLaunchRootTask是同options获取,我们这里options没有设置过mLaunchRootTask,因此为null
            final Task launchParentTask = getLaunchRootTask(resolvedWindowingMode, activityType,
                    options, sourceTask, launchFlags, candidateTask);
            //launchParentTask为null不进入该流程
            if (launchParentTask != null) {
                if (candidateTask.getParent() == null) {
                    launchParentTask.addChild(candidateTask, position);
                } else if (candidateTask.getParent() != launchParentTask) {
                    candidateTask.reparent(launchParentTask, position);
                }
            //candidateTask.getDisplayArea()和this都是默认的TaskDisplayArea,即DefaultTaskDisplayArea
            //两者相同,即candidateTask.getDisplayArea() != this 为false,因此不进入该流程
            } else if (candidateTask.getDisplayArea() != this 
                    || candidateTask.getRootTask().mReparentLeafTaskIfRelaunch) {
                if (candidateTask.getParent() == null) {
                    addChild(candidateTask, position);
                } else {
                    candidateTask.reparent(this, onTop);
                }
            }
            // Update windowing mode if necessary, e.g. launch into a different windowing mode.
            //windowingMode传递的是WINDOWING_MODE_FREEFORM
            //candidateTask自身就是rootTask
            //candidateTask前后windowingMode有发生变化
            if (windowingMode != WINDOWING_MODE_UNDEFINED && candidateTask.isRootTask()
                    && candidateTask.getWindowingMode() != windowingMode) {
                //ShellTransitions调用collect处理
                candidateTask.mTransitionController.collect(candidateTask);
                //设置task窗口模式
                candidateTask.setWindowingMode(windowingMode);
            }
            //返回candidateTask的rootTask
            return candidateTask.getRootTask();
        }
        return new Task.Builder(mAtmService)
                .setWindowingMode(windowingMode)
                .setActivityType(activityType)
                .setOnTop(onTop)
                .setParent(this)
                .setSourceTask(sourceTask)
                .setActivityOptions(options)
                .setLaunchFlags(launchFlags)
                .build();
    }

这里我们重点看这一段

            if (windowingMode != WINDOWING_MODE_UNDEFINED && candidateTask.isRootTask()
                    && candidateTask.getWindowingMode() != windowingMode) {
                candidateTask.mTransitionController.collect(candidateTask);
                candidateTask.setWindowingMode(windowingMode);
            }
            return candidateTask.getRootTask();

先解析下这个条件

  • windowingMode != WINDOWING_MODE_UNDEFINED windowingMode传递的是WINDOWING_MODE_FREEFORM,因此该条件为true。

  • candidateTask.isRootTask() 判断candidateTask是否是rootTask,关于这点前面有讲到,当前candidateTask就是rootTask,因此该条件为true。

  • candidateTask.getWindowingMode() != windowingMode) 获取当前candidateTask的windowingMode,判断该windowingMode和传递过来的windowingMode是否一致,我们这里还没有启动到自由窗口,因此candidateTask.getWindowingMode()不是WINDOWING_MODE_FREEFORM,所以不一致,即该条件为true。

综上,所有条件都满足,所有进入该条件。 其中candidateTask.mTransitionController.collect(candidateTask);涉及ShellTransitions,这里不涉及,暂不讲解。

最终通过candidateTask.setWindowingMode(windowingMode)方法给candidateTask的windowingMode设置成了WINDOWING_MODE_FREEFORM。

总结

mRootWindowContainer.anyTaskForId开始到TaskDisplayArea.getOrCreateRootTask结束,简单概括为一句话就是通过taskId获取对应task并设置该task的windowingMode为WINDOWING_MODE_FREEFORM(自由窗口)

移动桌面task至做前台

 mRootWindowContainer.getDefaultTaskDisplayArea().moveHomeRootTaskToFront(
                            "startActivityFromRecents");

代码路径:frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java

    void moveHomeRootTaskToFront(String reason) {
        final Task homeRootTask = getOrCreateRootHomeTask();
        if (homeRootTask != null) {
            homeRootTask.moveToFront(reason);
        }
    }

这个方法很简单就是获取桌面的rootTask然后通过homeRootTask.moveToFront(reason);,把桌面task至做前台。 关于Task.moveToFront方法在后面移动需要切换自由窗口Task移至最前台流程中说明。

移动需要切换自由窗口Task移至最前台

mService.moveTaskToFrontLocked(null /* appThread */,
                                null /* callingPackage */, task.mTaskId, 0, options);

调用到ActivityTaskManagerService.moveTaskToFrontLocked,传递对应参数。

ActivityTaskManagerService.moveTaskToFrontLocked

代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

    void moveTaskToFrontLocked(@Nullable IApplicationThread appThread,
            @Nullable String callingPackage, int taskId, int flags, SafeActivityOptions options) {
        ......
        try {
            //1.通过anyTaskForId方法获取task
            final Task task = mRootWindowContainer.anyTaskForId(taskId);
            ......
            //获取options,options不为null这把传递过来的SafeActivityOptions转换为ActivityOptions
            ActivityOptions realOptions = options != null
                    ? options.getOptions(mTaskSupervisor)
                    : null;
            //2.传递task和options,flags是前面传递过来的0
            mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
                    false /* forceNonResizable */);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
    }

这个方法主要就是做了两件事:

  1. 再次通过anyTaskForId方法获取task final Task task = mRootWindowContainer.anyTaskForId(taskId); 这里只传递了taskId,先进入到以下方法 代码路径:frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

        Task anyTaskForId(int id) {
            return anyTaskForId(id, MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE);
        }
    
        Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode) {
            return anyTaskForId(id, matchMode, null, !ON_TOP);
        }
        
        Task anyTaskForId(int id, @RootWindowContainer.AnyTaskForIdMatchTaskMode int matchMode,
                @Nullable ActivityOptions aOptions, boolean onTop) {
    

    从代码中我们可以看出,这一次通过anyTaskForId方法获取task传递的aOptions的值为null,这里就没有对获取的task进行一些相关设置,只是通过taskId获取该task,具体流程见前面的RootWindowContainer.anyTaskForId流程。

  2. 使用findTaskToMoveToFront方法传递对应参数

     mTaskSupervisor.findTaskToMoveToFront(task, flags, realOptions, "moveTaskToFront",
                        false /* forceNonResizable */);
    

    这里主要就是传递获取到的task和options,为后续移动task 到前台做准备。

继续看ActivityTaskSupervisor.findTaskToMoveToFront方法。

ActivityTaskSupervisor.findTaskToMoveToFront

代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

    /** This doesn't just find a task, it also moves the task to front. */
    void findTaskToMoveToFront(Task task, int flags, ActivityOptions options, String reason,
            boolean forceNonResizeable) {
        //获取当前task的rootTask
        Task currentRootTask = task.getRootTask();
        if (currentRootTask == null) {
            Slog.e(TAG, "findTaskToMoveToFront: can't move task="
                    + task + " to front. Root task is null");
            return;
        }

        try {
            if ((flags & ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) {
                mUserLeaving = true;
            }
            
            //延迟窗口布局
            mService.deferWindowLayout();
            //shell Transition相关
            final Transition newTransition = task.mTransitionController.isShellTransitionsEnabled()
                    ? task.mTransitionController.isCollecting() ? null
                    : task.mTransitionController.createTransition(TRANSIT_TO_FRONT) : null;
            task.mTransitionController.collect(task);
            reason = reason + " findTaskToMoveToFront";
            boolean reparented = false;
            //task.isResizeable(),判断task的大小是否可变化
            //canUseActivityOptionsLaunchBounds(options),判断当前options是否为null,并且判断系统是否支持画中画或者自由窗口
            if (task.isResizeable() && canUseActivityOptionsLaunchBounds(options)) {
                //再次获取RootTask
                Task targetRootTask =
                        mRootWindowContainer.getOrCreateRootTask(null, options, task, ON_TOP);

                //我们这边targetRootTask和currentRootTask是一样的,不进入此流程
                if (targetRootTask != currentRootTask) {
                    moveHomeRootTaskToFrontIfNeeded(flags, targetRootTask.getDisplayArea(), reason);
                    task.reparent(targetRootTask, ON_TOP, REPARENT_KEEP_ROOT_TASK_AT_FRONT,
                            !ANIMATE, DEFER_RESUME, reason);
                    currentRootTask = targetRootTask;
                    reparented = true;
                    // task.reparent() should already placed the task on top,
                    // still need moveTaskToFrontLocked() below for any transition settings.
                }
                // The resizeTask must be done after the task is moved to the correct root task,
                // because Task's setBounds() also updates dim layer's bounds, but that has
                // dependency on the root task.
                //从options中获取bounds并设置给task
                final Rect bounds = options.getLaunchBounds();
                task.setBounds(bounds);
            }

            //reparented值没有变化,为false,!reparented为true
            if (!reparented) {
                //这个方法的主要作用是移动桌面rootTask到前台,该流程没有执行,后续分析原因
                //这里传递的currentRootTask.getDisplayArea()指的是当前需要切换自由窗口task对应的TaskDisplayArea
                moveHomeRootTaskToFrontIfNeeded(flags, currentRootTask.getDisplayArea(), reason);
            }

            //获取当前task中最顶端的Activity
            final ActivityRecord r = task.getTopNonFinishingActivity();
            //移动需要切换自由窗口的task至前台
            currentRootTask.moveTaskToFront(task, false /* noAnimation */, options,
                    r == null ? null : r.appTimeTracker, reason);

            if (DEBUG_ROOT_TASK) Slog.d(TAG_ROOT_TASK,
                    "findTaskToMoveToFront: moved to front of root task=" + currentRootTask);

            //其他情况以及shell Transition相关
            ......
        } finally {
            mUserLeaving = false;
            //继续窗口布局,与前面mService.deferWindowLayout()延迟窗口布局相呼应。
            mService.continueWindowLayout();
        }
    }

从代码中我们可以看到,这个方法给我们需要切换到自由窗口的task设置bounds,然后再调用moveTaskToFront移动其到前台。

我们先看几个方法:

  • canUseActivityOptionsLaunchBounds(options) 参数options就是前面传递过来包含WindowingMode和Bounds的对象。

        boolean canUseActivityOptionsLaunchBounds(ActivityOptions options) {
            // We use the launch bounds in the activity options is the device supports freeform
            // window management or is launching into the root pinned task.
            if (options == null || options.getLaunchBounds() == null) {
                return false;
            }
    
            return (mService.mSupportsPictureInPicture
                    //我们的WindowingMode是WINDOWING_MODE_FREEFORM,不是画中画返回false
                    && options.getLaunchWindowingMode() == WINDOWING_MODE_PINNED)
                    || mService.mSupportsFreeformWindowManagement;
        }
    

    主要看看mSupportsPictureInPicturemSupportsFreeformWindowManagement这两个参数是怎么来的。

    代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java

        public void retrieveSettings(ContentResolver resolver) {
            final boolean freeformWindowManagement =
                    mContext.getPackageManager().hasSystemFeature(FEATURE_FREEFORM_WINDOW_MANAGEMENT)
                            || Settings.Global.getInt(
                            resolver, DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT, 0) != 0;
    
            final boolean supportsMultiWindow = ActivityTaskManager.supportsMultiWindow(mContext);
            final boolean supportsPictureInPicture = supportsMultiWindow &&
                    mContext.getPackageManager().hasSystemFeature(FEATURE_PICTURE_IN_PICTURE);
            ......
            synchronized (mGlobalLock) {
                mForceResizableActivities = forceResizable;
                ......
                final boolean multiWindowFormEnabled = freeformWindowManagement
                        || supportsSplitScreenMultiWindow
                        || supportsPictureInPicture
                        || supportsMultiDisplay;
                if ((supportsMultiWindow || forceResizable) && multiWindowFormEnabled) {
                    mSupportsMultiWindow = true;
                    mSupportsFreeformWindowManagement = freeformWindowManagement;
                    mSupportsSplitScreenMultiWindow = supportsSplitScreenMultiWindow;
                    mSupportsPictureInPicture = supportsPictureInPicture;
                    mSupportsExpandedPictureInPicture = supportsExpandedPictureInPicture;
                    mSupportsMultiDisplay = supportsMultiDisplay;
                } else {
                    mSupportsMultiWindow = false;
                    mSupportsFreeformWindowManagement = false;
                    mSupportsSplitScreenMultiWindow = false;
                    mSupportsPictureInPicture = false;
                    mSupportsExpandedPictureInPicture = false;
                    mSupportsMultiDisplay = false;
                }
          ......
    

    从代码中可以看出mSupportsPictureInPicturemSupportsFreeformWindowManagement等参数是用来判断系统是否支持画中画和浮窗等模式。

  • moveHomeRootTaskToFrontIfNeeded(flags, currentRootTask.getDisplayArea(), reason); flags是前面传递过来的0,currentRootTask.getDisplayArea()指的是当前需要切换自由窗口task对应的TaskDisplayArea。

        private void moveHomeRootTaskToFrontIfNeeded(int flags, TaskDisplayArea taskDisplayArea,
                String reason) {
            final Task focusedRootTask = taskDisplayArea.getFocusedRootTask();
            if ((taskDisplayArea.getWindowingMode() == WINDOWING_MODE_FULLSCREEN
                    && (flags & ActivityManager.MOVE_TASK_WITH_HOME) != 0)
                    || (focusedRootTask != null && focusedRootTask.isActivityTypeRecents())) {
                // We move root home task to front when we are on a fullscreen display area and
                // caller has requested the home activity to move with it. Or the previous root task
                // is recents.
                taskDisplayArea.moveHomeRootTaskToFront(reason);
            }
        }
    

    taskDisplayArea.getFocusedRootTask(),表示获取当前TaskDisplayArea下获取到焦点的RootTask,尚未进入到自由窗口前,当前界面在最近任务,因此当前取到焦点的RootTask就是Launcher(桌面)的RootTask,即focusedRootTask为Launcher的RootTask

    taskDisplayArea.getWindowingMode(),表示获取当前TaskDisplayArea的WindowingMode,即WINDOWING_MODE_FULLSCREEN

    ActivityManager.MOVE_TASK_WITH_HOME值为1,flags值为0,因此0&1,即值为0。

    focusedRootTask.isActivityTypeRecents(),判断focusedRootTask的类型是否是ACTIVITY_TYPE_RECENTS,我们这里focusedRootTask为Launcher的RootTask,因此其类型为ACTIVITY_TYPE_HOME

    综上,不符合条件,会进入到下面taskDisplayArea.moveHomeRootTaskToFront(reason)流程。

接下来继续看看切换task至前台的方法。

currentRootTask.moveTaskToFront(task, false /* noAnimation */, options,
                    r == null ? null : r.appTimeTracker, reason);

这里主要传递需要切换自由窗口的task走后续流程。

Task.moveTaskToFront

代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java

    final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
            AppTimeTracker timeTracker, String reason) {
        moveTaskToFront(tr, noAnimation, options, timeTracker, !DEFER_RESUME, reason);
    }

    final void moveTaskToFront(Task tr, boolean noAnimation, ActivityOptions options,
            AppTimeTracker timeTracker, boolean deferResume, String reason) {
        if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "moveTaskToFront: " + tr);

        ......

        try {
            // Defer updating the IME target since the new IME target will try to get computed
            // before updating all closing and opening apps, which can cause the ime target to
            // get calculated incorrectly.
            mDisplayContent.deferUpdateImeTarget();

            // Don't refocus if invisible to current user
            //获取当前需要切换自由窗口task顶部非finish的activity
            final ActivityRecord top = tr.getTopNonFinishingActivity();
            //top为空则通过positionChildAtTop获取顶部task
            if (top == null || !top.showToCurrentUser()) {
                positionChildAtTop(tr);
                if (top != null) {
                    mTaskSupervisor.mRecentTasks.add(top.getTask());
                }
                ActivityOptions.abort(options);
                return;
            }

            // Set focus to the top running activity of this task and move all its parents to top.
            //设置前面获取的top,把这个activity焦点并把其顶部Task移至最前端
            top.moveFocusableActivityToTop(reason);

            if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION, "Prepare to front transition: task=" + tr);
            ......

            //由前面!DEFER_RESUME的值false,传递过来因此,!deferResume为true
            if (!deferResume) {
                //确保正确的Activity始终处于前台Resumed状态
                mRootWindowContainer.resumeFocusedTasksTopActivities();
            }
        } finally {
            mDisplayContent.continueUpdateImeTarget();
        }
    }

这个方法主要是通过getTopNonFinishingActivity获取当前需要切换自由窗口task顶部非finish的activity,之后使用moveFocusableActivityToTop方法设置该activity焦点并把其顶部Task移至最前端。 我们先看看getTopNonFinishingActivity方法: 代码路径:frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java

    ActivityRecord getTopNonFinishingActivity() {
        return getTopNonFinishingActivity(true /* includeOverlays */);
    }

    /**
     * Returns the top-most non-finishing activity, even if the activity is NOT ok to show to
     * the current user.
     * @param includeOverlays whether the task overlay activity should be included.
     * @see #topRunningActivity(boolean)
     */
    ActivityRecord getTopNonFinishingActivity(boolean includeOverlays) {
        // Split into 2 to avoid object creation due to variable capture.
        if (includeOverlays) {
            return getActivity((r) -> !r.finishing);
        }
        return getActivity((r) -> !r.finishing && !r.isTaskOverlay());
    }

这个方式简单来说就是,从当前task顶部开始遍历,找到第一个finishing属性为false的Activity。 该方法与topRunningActivity()不同,这个方法不检查Activity是否对当前用户可见。

这里参数includeOverlays指的是,是否包含Task Overlay。true:包含Task Overlay的Activity;false:排除Task Overlay的Activity。 Task Overlay是一种特殊的Activity,它覆盖在任务上方,不属于任务正常流程的一部分,不参与任务栈的正常管理,例如:屏幕录制提示,画中画(PIP)控制界面。权限提示对话框,系统级别的覆盖层等。

后面继续分析top.moveFocusableActivityToTop(reason);方法。 这里的top指的是getTopNonFinishingActivity通过获取的需要切换为自由窗口task顶部非finish的activity,所以需要移动至最前端的也是这个activity。

ActivityRecord.moveFocusableActivityToTop

代码路径:frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java

    /**
     * Move activity with its root task to front and make the root task focused.
     * @param reason the reason to move to top
     * @return {@code true} if the root task is focusable and has been moved to top or the activity
     *         is not yet resumed while the root task is already on top, {@code false} otherwise.
     */
    boolean moveFocusableActivityToTop(String reason) {
        if (!isFocusable()) {
            ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: unfocusable "
                    + "activity=%s", this);
            return false;
        }

        //获取的就是前面top的rootTask,即需要切换为自由窗口的根task
        final Task rootTask = getRootTask();
        if (rootTask == null) {
            Slog.w(TAG, "moveFocusableActivityToTop: invalid root task: activity="
                    + this + " task=" + task);
            return false;
        }

        // If this activity already positions on the top focused task, moving the task to front
        // is not needed. But we still need to ensure this activity is focused because the
        // current focused activity could be another activity in the same Task if activities are
        // displayed on adjacent TaskFragments.
        //这里获取的是当前DisplayContent上焦点的Activity,此时为焦点的mFocusedApp是launcher
        final ActivityRecord currentFocusedApp = mDisplayContent.mFocusedApp;
        //currentFocusedApp != null,判断当前为焦点的Activity是否为null;
        //currentFocusedApp.task == task,判断当前焦点的task和需要切换为自由窗口的task是否相同
        //currentFocusedApp.task是launcher和需要切换为自由窗口的task不相同,因此不进入该流程
        if (currentFocusedApp != null && currentFocusedApp.task == task) {
            ......
        }

        ProtoLog.d(WM_DEBUG_FOCUS, "moveFocusableActivityToTop: activity=%s", this);

        //移动需要切换为自由窗口的rootTask至前台
        rootTask.moveToFront(reason, task);
        // Report top activity change to tracking services and WM
        //通知WMactivity层级发生变化
        if (mRootWindowContainer.getTopResumedActivity() == this) {
            mAtmService.setLastResumedActivityUncheckLocked(this, reason);
        }
        return true;
    }

我们来看这个方法中几个关键参数: mDisplayContent.mFocusedApp:表示当前DisplayContent上焦点的Activity。以从多任务启动自由窗口为例,尚未进入到自由窗口时,此时的焦点是launcher,因此这里mFocusedApp的值为launcher,代码中赋值给了currentFocusedAppcurrentFocusedApp.task:表示当前焦点Activity的task,如前面所说,这个Activity是launcher,因此这个task也是launcher对应的task。 this:由于该方法是由前面top.moveFocusableActivityToTop(reason)调用到此,top指的是getTopNonFinishingActivity通过获取的需要切换为自由窗口task顶部非finish的activity,因此this,就是这个activity。 task:ActivityRecord成员变量,就是this对应的task。

继续分析移动task到前台的方法。 rootTask.moveToFront(reason, task); 此处任然继续传递需要切换为自由窗口task。

Task.moveToFront

代码路径:frameworks/base/services/core/java/com/android/server/wm/Task.java

    /**
     * @param reason The reason for moving the root task to the front.
     * @param task If non-null, the task will be moved to the top of the root task.
     */
    @VisibleForTesting
    void moveToFront(String reason, Task task) {
        if (!isAttached()) {
            return;
        }
        mTransitionController.recordTaskOrder(this);

        final TaskDisplayArea taskDisplayArea = getDisplayArea();

        //!isActivityTypeHome(),判断当前task不是桌面类型,因此为true
        //returnsToHomeRootTask(),从代码中可以看出我们当前吃的窗口属于多窗口类型,因此为false
        //不进入此流程
        if (!isActivityTypeHome() && returnsToHomeRootTask()) {
            // Make sure the root home task is behind this root task since that is where we
            // should return to when this root task is no longer visible.
            taskDisplayArea.moveHomeRootTaskToFront(reason + " returnToHome");
        }

        final Task lastFocusedTask = isRootTask() ? taskDisplayArea.getFocusedRootTask() : null;
        if (task == null) {
            task = this;
        }
    
        //通过positionChildAt,传递参数POSITION_TOP,把task移动到最前端
        task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */);
        taskDisplayArea.updateLastFocusedRootTask(lastFocusedTask, reason);
    }

这个方法中最为关键就是这段代码,也是真正把task移动到最前端的代码。 task.getParent().positionChildAt(POSITION_TOP, task, true /* includingParents */); 先来看看参数: task:需要切换自由窗口的task。 task.getParent():自由窗口task的父节点,即DefaultTaskDisplayArea。 POSITION_TOP:需要移动task到顶端的参数。

WindowContainer.positionChildAt

我们来看看这个positionChildAt是怎么把task移动到顶部的 代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

    /**
     * Move a child from it's current place in siblings list to the specified position,
     * with an option to move all its parents to top.
     * @param position Target position to move the child to.
     * @param child Child to move to selected position.
     * @param includingParents Flag indicating whether we need to move the entire branch of the
     *                         hierarchy when we're moving a child to {@link #POSITION_TOP} or
     *                         {@link #POSITION_BOTTOM}. When moving to other intermediate positions
     *                         this flag will do nothing.
     */
    @CallSuper
    void positionChildAt(int position, E child, boolean includingParents) {
        if (child.getParent() != this) {
            throw new IllegalArgumentException("positionChildAt: container=" + child.getName()
                    + " is not a child of container=" + getName()
                    + " current parent=" + child.getParent());
        }

        if (position >= mChildren.size() - 1) {
            position = POSITION_TOP;
        } else if (position <= 0) {
            position = POSITION_BOTTOM;
        }

        switch (position) {
            case POSITION_TOP:
                //判断mChildren中最后一项是否是当前child
                if (mChildren.peekLast() != child) {
                    //移除child
                    mChildren.remove(child);
                    //添加child
                    mChildren.add(child);
                    //通知child位置发生变化
                    onChildPositionChanged(child);
                }
                //includingParents,传递过来值为true
                //getParent(),当前this是DefaultTaskDisplayArea,其父节点不为空
                if (includingParents && getParent() != null) {
                    //此时DefaultTaskDisplayArea为child继续递归遍历
                    getParent().positionChildAt(POSITION_TOP, this /* child */,
                            true /* includingParents */);
                }
                break;
            case POSITION_BOTTOM:
                //判断mChildren中第一项是否是当前child
                if (mChildren.peekFirst() != child) {
                    mChildren.remove(child);
                    mChildren.addFirst(child);
                    onChildPositionChanged(child);
                }
                if (includingParents && getParent() != null) {
                    getParent().positionChildAt(POSITION_BOTTOM, this /* child */,
                            true /* includingParents */);
                }
                break;
            default:
                // TODO: Removing the child before reinserting requires the caller to provide a
                //       position that takes into account the removed child (if the index of the
                //       child < position, then the position should be adjusted). We should consider
                //       doing this adjustment here and remove any adjustments in the callers.
                //判断mChildren中child的位置是否在position
                if (mChildren.indexOf(child) != position) {
                    mChildren.remove(child);
                    mChildren.add(position, child);
                    onChildPositionChanged(child);
                }
        }
    }

由于我们是通过task.getParent()调用到positionChildAt,因此这里mChildren指的就是task.getParent()之下的所有子节点,即DefaultTaskDisplayArea下的子节点。 实际上这个方法就是把DefaultTaskDisplayArea按照新的顺序重新添加了一遍其子节点。

我们结合dumpsys containers来理解这段代码。

mChildren.peekLast() != child,判断mChildren中最后一项是否是当前child,在最后一项的就是最顶端; 反之mChildren.peekFirst() != child,判断mChildren中第一项是否是当前child,在第一项的就是最底端。 如下图所示:

切换为自由窗口前: 在这里插入图片描述 我们可以看到切换自由窗口之前,Task=1就是最后一项,其编号为#2

切换为自由窗口后 在这里插入图片描述 我们可以看到切换自由窗口之前,Task=1337就是最后一项,其编号为#2

总结

ActivityTaskManagerService.moveTaskToFrontLocked开始,到WindowContainer.positionChildAt结束,简单概括为一句话就是传递需要切换为自由窗口task,确定其顶部activity,将该task移动至最前端显示

总结

本文主要讲述了从多任务中启动自由窗口的流程,简单可以概括为:

  1. 桌面端确定需要进入自由窗口的task并传递相应参数。
  2. 通过SystemUI跨进程调用到system_server侧处理。
  3. system_server端通过桌面端传递的参数设置其windowingMode,并将该task移动至前台显示。