Android14 - AMS之Activity启动过程(2)

上篇梳理到:

TaskDisplayArea和Task的复用与创建

TaskDisplayArea

executeRequest后,随后调用startActivityUnchecked,进而调用startActivityInner。

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

private int startActivityUnchecked(final ActivityRecord r, ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags, ActivityOptions options, Task inTask, TaskFragment inTaskFragment, @BalCode int balCode, NeededUriGrants intentGrants, int realCallingUid) {
    ...
    result = startActivityInner(r, sourceRecord, voiceSession, voiceInteractor, startFlags, options, inTask, inTaskFragment, balCode, intentGrants, realCallingUid);
}

int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord, 
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            int startFlags, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, @BalCode int balCode,
            NeededUriGrants intentGrants, int realCallingUid) {
        // 位置1:初始化启动参数
        setInitialState(r, options, inTask, inTaskFragment, startFlags, sourceRecord,
                voiceSession, voiceInteractor, balCode, realCallingUid);
        computeLaunchingTaskFlags();
        mIntent.setFlags(mLaunchFlags);
        
        ...
        // 位置2:mPreferredTaskDisplayArea为RootWindowContainer.getDefaultTaskDisplayArea()
        final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
        final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
        final Task reusedTask = getReusableTask();
        ...
        final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
        final boolean newTask = targetTask == null;
        mTargetTask = targetTask;    
        
        // 位置3:计算WindowingMode
        computeLaunchParams(r, sourceRecord, targetTask);
        
        int startResult = isAllowedToStart(r, newTask, targetTask);        
        
        ...
        final ActivityRecord targetTaskTop = newTask
                ? null : targetTask.getTopNonFinishingActivity();
        if (targetTaskTop != null) {
            // Removes the existing singleInstance activity in another task (if any) while
            // launching a singleInstance activity on sourceRecord's task.
            if (LAUNCH_SINGLE_INSTANCE == mLaunchMode && mSourceRecord != null
                    && targetTask == mSourceRecord.getTask()) {
                final ActivityRecord activity = mRootWindowContainer.findActivity(mIntent,
                        mStartActivity.info, false);
                if (activity != null && activity.getTask() != targetTask) {
                    activity.destroyIfPossible("Removes redundant singleInstance");
                }
            }
            recordTransientLaunchIfNeeded(targetTaskTop);
            // Recycle the target task for this launch.
            startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants);
            if (startResult != START_SUCCESS) {
                return startResult;
            }
        } else {
            mAddingToTask = true;
        }

        if (mTargetRootTask == null) {
            mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
                    mOptions);
        }
        if (newTask) {
            final Task taskToAffiliate = (mLaunchTaskBehind && mSourceRecord != null)
                    ? mSourceRecord.getTask() : null;
            setNewTask(taskToAffiliate);
        } else if (mAddingToTask) {
            addOrReparentStartingActivity(targetTask, "adding to task");
        }
        ...
        mTargetRootTask.startActivityLocked(mStartActivity, topRootTask, newTask, isTaskSwitch,
                mOptions, sourceRecord);
    // **** 位置4
    if (mDoResume) {
		final ActivityRecord topTaskActivity = startedTask.topRunningActivityLocked();
		if (!mTargetRootTask.isTopActivityFocusable()
				|| (topTaskActivity != null && topTaskActivity.isTaskOverlay()
				&& mStartActivity != topTaskActivity)) {
			// If the activity is not focusable, we can't resume it, but still would like to
			// make sure it becomes visible as it starts (this will also trigger entry
			// animation). An example of this are PIP activities.
			// Also, we don't want to resume activities in a task that currently has an overlay
			// as the starting activity just needs to be in the visible paused state until the
			// over is removed.
			// Passing {@code null} as the start parameter ensures all activities are made
			// visible.
			mTargetRootTask.ensureActivitiesVisible(null /* starting */,
					0 /* configChanges */, !PRESERVE_WINDOWS);
			// Go ahead and tell window manager to execute app transition for this activity
			// since the app transition will not be triggered through the resume channel.
			mTargetRootTask.mDisplayContent.executeAppTransition();
		} else {
			// If the target root-task was not previously focusable (previous top running
			// activity on that root-task was not visible) then any prior calls to move the
			// root-task to the will not update the focused root-task.  If starting the new
			// activity now allows the task root-task to be focusable, then ensure that we
			// now update the focused root-task accordingly.
			if (!mAvoidMoveToFront && mTargetRootTask.isTopActivityFocusable()
					&& !mRootWindowContainer.isTopDisplayFocusedRootTask(mTargetRootTask)) {
				mTargetRootTask.moveToFront("startActivityInner");
			}
			mRootWindowContainer.resumeFocusedTasksTopActivities(
					mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
		}
	}
	mRootWindowContainer.updateUserRootTask(mStartActivity.mUserId, mTargetRootTask);

	// Update the recent tasks list immediately when the activity starts
	mSupervisor.mRecentTasks.add(startedTask);
	mSupervisor.handleNonResizableTaskIfNeeded(startedTask,
			mPreferredWindowingMode, mPreferredTaskDisplayArea, mTargetRootTask);

	// If Activity's launching into PiP, move the mStartActivity immediately to pinned mode.
	// Note that mStartActivity and source should be in the same Task at this point.
	if (mOptions != null && mOptions.isLaunchIntoPip()
			&& sourceRecord != null && sourceRecord.getTask() == mStartActivity.getTask()
			&& balCode != BAL_BLOCK) {
		mRootWindowContainer.moveActivityToPinnedRootTask(mStartActivity,
				sourceRecord, "launch-into-pip");
	}

	return START_SUCCESS;
}

mPreferredTaskDisplayArea的赋值来自setInitialState, 

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

private void setInitialState(ActivityRecord r, ActivityOptions options, Task inTask,
            TaskFragment inTaskFragment, int startFlags,
            ActivityRecord sourceRecord, IVoiceInteractionSession voiceSession,
            IVoiceInteractor voiceInteractor, @BalCode int balCode, int realCallingUid) {
            
    mStartActivity = r;
	mIntent = r.intent;
	mOptions = options;
	mCallingUid = r.launchedFromUid;
	mRealCallingUid = realCallingUid;
	mSourceRecord = sourceRecord;
	mSourceRootTask = mSourceRecord != null ? mSourceRecord.getRootTask() : null;
	mVoiceSession = voiceSession;
	mVoiceInteractor = voiceInteractor;
	mBalCode = balCode;

	mLaunchParams.reset();

    mSupervisor.getLaunchParamsController().calculate(inTask, r.info.windowLayout, r, sourceRecord, options, mRequest, PHASE_DISPLAY, mLaunchParams);
    mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea()
                ? mLaunchParams.mPreferredTaskDisplayArea
                : mRootWindowContainer.getDefaultTaskDisplayArea();
    mPreferredWindowingMode = mLaunchParams.mWindowingMode;

    mLaunchMode = r.launchMode;

    mLaunchFlags = adjustLaunchFlagsToDocumentMode(
                r, LAUNCH_SINGLE_INSTANCE == mLaunchMode,
                LAUNCH_SINGLE_TASK == mLaunchMode, mIntent.getFlags());

首先注意mStartActivity = r;赋值mStartActivity就是新new的ActivityRecord后面

mSupervisor.getLaunchParamsController()来自ActivityTaskSupervisor的初始化,调用其calculate方法。注意,这里传入的PHASE_DISPLAY:

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

public void initialize() {
    mLaunchParamsController = new LaunchParamsController(mService, mLaunchParamsPersister);
    mLaunchParamsController.registerDefaultModifiers(this);
}

void registerDefaultModifiers(ActivityTaskSupervisor supervisor) {
        // {@link TaskLaunchParamsModifier} handles window layout preferences.
        registerModifier(new TaskLaunchParamsModifier(supervisor));
        if (DesktopModeLaunchParamsModifier.isDesktopModeSupported()) {
            // {@link DesktopModeLaunchParamsModifier} handles default task size changes
            registerModifier(new DesktopModeLaunchParamsModifier());
        }
    }

我们看到,new了一个LaunchParamsController后,注册了Modifiers为new TaskLaunchParamsModifier。

LaunchParamsController的calculate代码:

platform/frameworks/base/services/core/java/com/android/server/wm/LaunchParamsController.java

void calculate(Task task, WindowLayout layout, ActivityRecord activity, ActivityRecord source,
            ActivityOptions options, @Nullable Request request, int phase, LaunchParams result) {
     if (task != null || activity != null) {
		mPersister.getLaunchParams(task, activity, result);
	}

	// We start at the last registered {@link LaunchParamsModifier} as this represents
	// The modifier closest to the product level. Moving back through the list moves closer to
	// the platform logic.
	for (int i = mModifiers.size() - 1; i >= 0; --i) {
		mTmpCurrent.set(result);
		mTmpResult.reset();
		final LaunchParamsModifier modifier = mModifiers.get(i);

		switch(modifier.onCalculate(task, layout, activity, source, options, request, phase,
				mTmpCurrent, mTmpResult)) {
			case RESULT_SKIP:
				// Do not apply any results when we are told to skip
				continue;
			case RESULT_DONE:
				// Set result and return immediately.
				result.set(mTmpResult);
				return;
			case RESULT_CONTINUE:
				// Set result and continue
				result.set(mTmpResult);
				break;
		}
	}

	if (activity != null && activity.requestedVrComponent != null) {
		// Check if the Activity is a VR activity. If so, it should be launched in main display.
		result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
				.getDefaultTaskDisplayArea();
	} else if (mService.mVr2dDisplayId != INVALID_DISPLAY) {
		// Get the virtual display ID from ActivityTaskManagerService. If that's set we
		// should always use that.
		result.mPreferredTaskDisplayArea = mService.mRootWindowContainer
				.getDisplayContent(mService.mVr2dDisplayId).getDefaultTaskDisplayArea();
	}           
}

mModifiers目前只有一个成员,就是TaskLaunchParamsModifier,这里调用其onCalculate:

@Override
    public int onCalculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
            @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
            @Nullable ActivityOptions options, @Nullable Request request, int phase,
            LaunchParams currentParams, LaunchParams outParams) {
        initLogBuilder(task, activity);
        final int result = calculate(task, layout, activity, source, options, request, phase,
                currentParams, outParams);
        outputLog();
        return result;
    }

    private int calculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
            @Nullable ActivityRecord activity, @Nullable ActivityRecord source,
            @Nullable ActivityOptions options, @Nullable Request request, int phase,
            LaunchParams currentParams, LaunchParams outParams) {
        final ActivityRecord root;
        if (task != null) {
            root = task.getRootActivity() == null ? activity : task.getRootActivity();
        } else {
            root = activity;
        }

        if (root == null) {
            // There is a case that can lead us here. The caller is moving the top activity that is
            // in a task that has multiple activities to PIP mode. For that the caller is creating a
            // new task to host the activity so that we only move the top activity to PIP mode and
            // keep other activities in the previous task. There is no point to apply the launch
            // logic in this case.
            return RESULT_SKIP;
        }

        // STEP 1: Determine the suggested display area to launch the activity/task.
        final TaskDisplayArea suggestedDisplayArea = getPreferredLaunchTaskDisplayArea(task,
                options, source, currentParams, activity, request);
        outParams.mPreferredTaskDisplayArea = suggestedDisplayArea;
        final DisplayContent display = suggestedDisplayArea.mDisplayContent;
        if (DEBUG) {
            appendLog("display-id=" + display.getDisplayId()
                    + " task-display-area-windowing-mode=" + suggestedDisplayArea.getWindowingMode()
                    + " suggested-display-area=" + suggestedDisplayArea);
        }

        if (phase == PHASE_DISPLAY) {
            return RESULT_CONTINUE;
        }
        ...
}

由于我们一开始的场景是从Context启动一个Activity,source、option、inTask等参数都是空,currentParams(从ActivityStarter.setInitialState构建而来)的mPreferredTaskDisplayArea也是空,所以getPreferredLaunchTaskDisplayArea方法最终会从getFallbackDisplayAreaForActivity方法获取mPreferredTaskDisplayArea:

/**
 * Calculates the default {@link TaskDisplayArea} for a task. We attempt to put the activity
 * within the same display area if possible. The strategy is to find the display in the
 * following order:
 *
 * <ol>
 *     <li>The display area of the top activity from the launching process will be used</li>
 *     <li>The display area of the top activity from the real launching process will be used
 *     </li>
 *     <li>Default display area from the associated root window container.</li>
 * </ol>
 * @param activityRecord the activity being started
 * @param request optional {@link Request} made to start the activity record
 * @return {@link TaskDisplayArea} to house the task
 */
private TaskDisplayArea getFallbackDisplayAreaForActivity(
		@NonNull ActivityRecord activityRecord, @Nullable Request request) {

	WindowProcessController controllerFromLaunchingRecord = mSupervisor.mService
			.getProcessController(activityRecord.launchedFromPid,
					activityRecord.launchedFromUid);
	final TaskDisplayArea displayAreaForLaunchingRecord = controllerFromLaunchingRecord == null
			? null : controllerFromLaunchingRecord.getTopActivityDisplayArea();
	if (displayAreaForLaunchingRecord != null) {
		return displayAreaForLaunchingRecord;
	}

	WindowProcessController controllerFromProcess = mSupervisor.mService.getProcessController(
			activityRecord.getProcessName(), activityRecord.getUid());
	final TaskDisplayArea displayAreaForRecord = controllerFromProcess == null ? null
			: controllerFromProcess.getTopActivityDisplayArea();
	if (displayAreaForRecord != null) {
		return displayAreaForRecord;
	}

	WindowProcessController controllerFromRequest = request == null ? null : mSupervisor
			.mService.getProcessController(request.realCallingPid, request.realCallingUid);
	final TaskDisplayArea displayAreaFromSourceProcess = controllerFromRequest == null ? null
			: controllerFromRequest.getTopActivityDisplayArea();
	if (displayAreaFromSourceProcess != null) {
		return displayAreaFromSourceProcess;
	}

	return mSupervisor.mRootWindowContainer.getDefaultTaskDisplayArea();
}

首先会根据activityRecord.launchedFromPid,也就是CallingPid(在ActivityStarter.executeReauest()时设置),或activityRecord.getProcessName,或者realCallingPid,去获取其所在的TopActivity所属的DisplayArea。比如如果是从Laucnher启动的一个新的Activity,那么此时会使用Launcher顶部Activity所属的DisplayArea。如果以上都没有找到, 则使用mRootWindowContainer的默认DefaultTaskDisplayArea。通常这个DefaultTaskDisplayArea是对应一个屏幕的DisplayContent。

回到calculate(方法。现在我们得到了suggestedDisplayArea,赋值给outParams.mPreferredTaskDisplayArea。由于上面我们传入的phase是PHASE_DISPLAY,所以下面直接返回return RESULT_CONTINUE;

再继续返回LaunchParamsController的calculate(..), RESULT_CONTINUE对应的逻辑是result.set(mTmpResult);,就是将刚才outParams的值设置到in-out型参数result,返回给上层。而后面的activity.requestedVrComponent 等判断逻辑,都是跟VR场景有关的,不探讨。

回到ActivityStarter的startActivityInner, 假如我们现在只有一个物理屏幕,那么final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();返回的是默认DisplayContent的getFocusedRootTask:

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

@Nullable
Task getFocusedRootTask() {
    return getItemFromTaskDisplayAreas(TaskDisplayArea::getFocusedRootTask);
}
platform/frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

/**
 * Finds the first non {@code null} return value from calling the callback on all
 * {@link TaskDisplayArea} at or below this container.
 * @param callback Applies on each {@link TaskDisplayArea} found and stops the search if it
 *                 returns non {@code null}.
 * @param traverseTopToBottom If {@code true}, traverses the hierarchy from top-to-bottom in
 *                            terms of z-order, else from bottom-to-top.
 * @return the first returned object that is not {@code null}. Returns {@code null} if not
 *         found.
 */
@Nullable
<R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
		boolean traverseTopToBottom) {
	int childCount = mChildren.size();
	int i = traverseTopToBottom ? childCount - 1 : 0;
	while (i >= 0 && i < childCount) {
		R result = (R) mChildren.get(i)
				.getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
		if (result != null) {
			return result;
		}
		i += traverseTopToBottom ? -1 : 1;
	}
	return null;
}
platform/frameworks/base/services/core/java/com/android/server/wm/DisplayArea.java

@Nullable
@Override
<R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
		boolean traverseTopToBottom) {
	// Only DisplayArea of Type.ANY may contain TaskDisplayArea as children.
	if (mType != DisplayArea.Type.ANY) {
		return null;
	}

	int childCount = mChildren.size();
	int i = traverseTopToBottom ? childCount - 1 : 0;
	while (i >= 0 && i < childCount) {
		T child = mChildren.get(i);
		// Only traverse if the child is a DisplayArea.
		if (child.asDisplayArea() != null) {
			R result = (R) child.asDisplayArea()
					.getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
			if (result != null) {
				return result;
			}
		}
		i += traverseTopToBottom ? -1 : 1;
	}
	return null;
}
platform/frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java

@Nullable
@Override
<R> R getItemFromTaskDisplayAreas(Function<TaskDisplayArea, R> callback,
		boolean traverseTopToBottom) {
	if (traverseTopToBottom) {
		final R item = super.getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
		return item != null ? item : callback.apply(this);
	} else {
		final R item = callback.apply(this);
		return item != null
				? item
				: super.getItemFromTaskDisplayAreas(callback, traverseTopToBottom);
	}
}

getItemFromTaskDisplayAreas是一个递归查询某TaskDisplayArea的首个非空child的方法。其参数callback是一个Function类型的参数,用于将TaskDisplayArea转换为R类型。

首先,getItemFromTaskDisplayAreas是在WindowContainer声明的,DisplayArea、TaskDisplayArea重载了getItemFromTaskDisplayAreas。所有的窗口类型(WindowToken、DisplayContent、DisplayArea、TaskDisplayArea等)都是WindowContainer的子类,而DisplayContent继承于DisplayArea,DisplayArea继承WindowContainer。所以这里首先执行的是DisplayArea的getItemFromTaskDisplayAreas。而TaskDisplayArea是DisplayContent的child,所以接下来会调用TaskDisplayArea的getItemFromTaskDisplayAreas,此时在递归深度优先获取到首个非空child后,会调用callbak。

由于getFocusedRootTask()传入的callback是TaskDisplayArea::getFocusedRootTask,这里是一个lamda语法,看下这个方法:

platform/frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java

Task getFocusedRootTask() {
	if (mPreferredTopFocusableRootTask != null) {
		return mPreferredTopFocusableRootTask;
	}

	for (int i = mChildren.size() - 1; i >= 0; --i) {
		final WindowContainer child = mChildren.get(i);
		if (child.asTaskDisplayArea() != null) {
			final Task rootTask = child.asTaskDisplayArea().getFocusedRootTask();
			if (rootTask != null) {
				return rootTask;
			}
			continue;
		}

		final Task rootTask = mChildren.get(i).asTask();
		if (rootTask.isFocusableAndVisible()) {
			return rootTask;
		}
	}

	return null;
}

如果该TaskDisplayArea还有子TaskDisplayArea,那么会找子TaskDisplayArea的Task,否则就遍历当前TaskDisplayArea的子Task,找到isFocusableAndVisible的,返回。

这里的逻辑有点绕,关于窗口体系,请见Android12 - WMS之WindowContainer树(DisplayArea)-CSDN博客

复用Task

Task有嵌套关系。RootTask包含LeafTask。

回到startActivityInner,拿到prevTopRootTask后,Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;

platform/frameworks/base/services/core/java/com/android/server/wm/Task.java

/** Return the top-most leaf-task under this one, or this task if it is a leaf. */
public Task getTopLeafTask() {
	for (int i = mChildren.size() - 1; i >= 0; --i) {
		final Task child = mChildren.get(i).asTask();
		if (child == null) continue;
		return child.getTopLeafTask();
	}
	return this;
}

如果该prevTopRootTask有Child Task,则返回最顶部(最后加入的)Task,否则就返回自身。那么prevTopTask就是当前最顶部task。

final Task reusedTask = getReusableTask();获取可重复使用的Task。


/**
 * Decide whether the new activity should be inserted into an existing task. Returns null
 * if not or an ActivityRecord with the task into which the new activity should be added.
 */
private Task getReusableTask() {
	// If a target task is specified, try to reuse that one
	if (mOptions != null && mOptions.getLaunchTaskId() != INVALID_TASK_ID) {
		Task launchTask = mRootWindowContainer.anyTaskForId(mOptions.getLaunchTaskId());
		if (launchTask != null) {
			return launchTask;
		}
		return null;
	}

	// We may want to try to place the new activity in to an existing task.  We always
	// do this if the target activity is singleTask or singleInstance; we will also do
	// this if NEW_TASK has been requested, and there is not an additional qualifier telling
	// us to still place it in a new task: multi task, always doc mode, or being asked to
	// launch this as a new task behind the current one.
	boolean putIntoExistingTask = ((mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0 &&
			(mLaunchFlags & FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
			|| isLaunchModeOneOf(LAUNCH_SINGLE_INSTANCE, LAUNCH_SINGLE_TASK);
	// If bring to front is requested, and no result is requested and we have not been given
	// an explicit task to launch in to, and we can find a task that was started with this
	// same component, then instead of launching bring that one to the front.
	putIntoExistingTask &= mInTask == null && mStartActivity.resultTo == null;
	ActivityRecord intentActivity = null;
	if (putIntoExistingTask) {
		if (LAUNCH_SINGLE_INSTANCE == mLaunchMode) {
			// There can be one and only one instance of single instance activity in the
			// history, and it is always in its own unique task, so we do a special search.
			intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
				   mStartActivity.isActivityTypeHome());
		} else if ((mLaunchFlags & FLAG_ACTIVITY_LAUNCH_ADJACENT) != 0) {
			// For the launch adjacent case we only want to put the activity in an existing
			// task if the activity already exists in the history.
			intentActivity = mRootWindowContainer.findActivity(mIntent, mStartActivity.info,
					!(LAUNCH_SINGLE_TASK == mLaunchMode));
		} else {
			// Otherwise find the best task to put the activity in.
			intentActivity =
					mRootWindowContainer.findTask(mStartActivity, mPreferredTaskDisplayArea);
		}
	}

	if (intentActivity != null && mLaunchMode == LAUNCH_SINGLE_INSTANCE_PER_TASK
			&& !intentActivity.getTask().getRootActivity().mActivityComponent.equals(
			mStartActivity.mActivityComponent)) {
		// The task could be selected due to same task affinity. Do not reuse the task while
		// starting the singleInstancePerTask activity if it is not the task root activity.
		intentActivity = null;
	}

	if (intentActivity != null
			&& (mStartActivity.isActivityTypeHome() || intentActivity.isActivityTypeHome())
			&& intentActivity.getDisplayArea() != mPreferredTaskDisplayArea) {
		// Do not reuse home activity on other display areas.
		intentActivity = null;
	}

	return intentActivity != null ? intentActivity.getTask() : null;
}

 如果mOptions.getLaunchTaskId指定了复用的TaskId(通过在startActivity时指定ActivityOption),则直接返回。否则根据Intent flag判断是否需要使用已存在的task。

如果getReusableTask返回空,则继续使用computeTargetTask查看是否有可复用的task

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

/** Returns the leaf task where the target activity may be placed. */
private Task computeTargetTask() {
	if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
			&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
		// A new task should be created instead of using existing one.
		return null;
	} else if (mSourceRecord != null) {
		return mSourceRecord.getTask();
	} else if (mInTask != null) {
		// The task is specified from AppTaskImpl, so it may not be attached yet.
		if (!mInTask.isAttached()) {
			// Attach the task to display area. Ignore the returned root task (though usually
			// they are the same) because "target task" should be leaf task.
			getOrCreateRootTask(mStartActivity, mLaunchFlags, mInTask, mOptions);
		}
		return mInTask;
	} else {
		final Task rootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, null /* task */,
				mOptions);
		final ActivityRecord top = rootTask.getTopNonFinishingActivity();
		if (top != null) {
			return top.getTask();
		} else {
			// Remove the root task if no activity in the root task.
			rootTask.removeIfPossible("computeTargetTask");
		}
	}
	return null;
}

从之前的参数整理部分,我们在我们启动的这个场景,resultTo和mInTask是空,此时mAddingToTask是false。如果用的是FLAG_ACTIVITY_NEW_TASK标签,则直接就放回null了。本方法的其他的条件中,也是想复用task。所以这个方法本意还是想复用。如果没有复用的话,mTargetRootTask还是空。

更新WindowingMode和mBounds

回到startActivityInner继续往下,在【位置3】处,调用了如下代码:

computeLaunchParams(r, sourceRecord, targetTask);

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

private void computeLaunchParams(ActivityRecord r, ActivityRecord sourceRecord,
		Task targetTask) {
	mSupervisor.getLaunchParamsController().calculate(targetTask, r.info.windowLayout, r,
			sourceRecord, mOptions, mRequest, PHASE_BOUNDS, mLaunchParams);
	mPreferredTaskDisplayArea = mLaunchParams.hasPreferredTaskDisplayArea()
			? mLaunchParams.mPreferredTaskDisplayArea
			: mRootWindowContainer.getDefaultTaskDisplayArea();
	mPreferredWindowingMode = mLaunchParams.mWindowingMode;
}

这里看到,会再次调用mSupervisor.getLaunchParamsController().calculate, 二此时传入的phase是PHASE_BOUNDS。那么我们回到TaskLaunchParamsModifier.calculate方法。上次我们走到PHASE_DISPLAY后return了。这次继续往下走:

platform/frameworks/base/services/core/java/com/android/server/wm/TaskLaunchParamsModifier.java

private int calculate(@Nullable Task task, @Nullable ActivityInfo.WindowLayout layout,
		@Nullable ActivityRecord activity, @Nullable ActivityRecord source,
		@Nullable ActivityOptions options, @Nullable Request request, int phase,
		LaunchParams currentParams, LaunchParams outParams) {
	final ActivityRecord root;
	if (task != null) {
		root = task.getRootActivity() == null ? activity : task.getRootActivity();
	} else {
		root = activity;
	}

	if (root == null) {
		// There is a case that can lead us here. The caller is moving the top activity that is
		// in a task that has multiple activities to PIP mode. For that the caller is creating a
		// new task to host the activity so that we only move the top activity to PIP mode and
		// keep other activities in the previous task. There is no point to apply the launch
		// logic in this case.
		return RESULT_SKIP;
	}

	// STEP 1: Determine the suggested display area to launch the activity/task.
	final TaskDisplayArea suggestedDisplayArea = getPreferredLaunchTaskDisplayArea(task,
			options, source, currentParams, activity, request);
	outParams.mPreferredTaskDisplayArea = suggestedDisplayArea;
	final DisplayContent display = suggestedDisplayArea.mDisplayContent;
	if (DEBUG) {
		appendLog("display-id=" + display.getDisplayId()
				+ " task-display-area-windowing-mode=" + suggestedDisplayArea.getWindowingMode()
				+ " suggested-display-area=" + suggestedDisplayArea);
	}

	if (phase == PHASE_DISPLAY) {
		return RESULT_CONTINUE;
	}
   // *** 本次我们继续往下执行
	// STEP 2: Resolve launch windowing mode.
	// STEP 2.1: Determine if any parameter can specify initial bounds/windowing mode. That
	// might be the launch bounds from activity options, or size/gravity passed in layout. It
	// also treats the launch windowing mode in options and source activity windowing mode in
	// some cases as a suggestion for future resolution.
	int launchMode = options != null ? options.getLaunchWindowingMode()
			: WINDOWING_MODE_UNDEFINED;
	// In some cases we want to use the source's windowing mode as the default value, e.g. when
	// source is a freeform window in a fullscreen display launching an activity on the same
	// display.
	if (launchMode == WINDOWING_MODE_UNDEFINED
			&& canInheritWindowingModeFromSource(display, suggestedDisplayArea, source)) {
		// The source's windowing mode may be different from its task, e.g. activity is set
		// to fullscreen and its task is pinned windowing mode when the activity is entering
		// pip.
		launchMode = source.getTask().getWindowingMode();
		if (DEBUG) {
			appendLog("inherit-from-source="
					+ WindowConfiguration.windowingModeToString(launchMode));
		}
	}
	// If the launch windowing mode is still undefined, inherit from the target task if the
	// task is already on the right display area (otherwise, the task may be on a different
	// display area that has incompatible windowing mode or the task organizer request to
	// disassociate the leaf task if relaunched and reparented it to TDA as root task).
	if (launchMode == WINDOWING_MODE_UNDEFINED
			&& task != null && task.getTaskDisplayArea() == suggestedDisplayArea
			&& !task.getRootTask().mReparentLeafTaskIfRelaunch) {
		launchMode = task.getWindowingMode();
		if (DEBUG) {
			appendLog("inherit-from-task="
					+ WindowConfiguration.windowingModeToString(launchMode));
		}
	}
	// hasInitialBounds is set if either activity options or layout has specified bounds. If
	// that's set we'll skip some adjustments later to avoid overriding the initial bounds.
	boolean hasInitialBounds = false;
	// hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow is set if the outParams.mBounds
	// is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
	// different, we should recalculating the bounds.
	boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = false;
	// Note that initial bounds needs to be set to fullscreen tasks too as it's used as restore
	// bounds.
	final boolean canCalculateBoundsForFullscreenTask =
			canCalculateBoundsForFullscreenTask(suggestedDisplayArea, launchMode);
	final boolean canApplyFreeformWindowPolicy =
			canApplyFreeformWindowPolicy(suggestedDisplayArea, launchMode);
	final boolean canApplyWindowLayout = layout != null
			&& (canApplyFreeformWindowPolicy || canCalculateBoundsForFullscreenTask);
	final boolean canApplyBoundsFromActivityOptions =
			mSupervisor.canUseActivityOptionsLaunchBounds(options)
					&& (canApplyFreeformWindowPolicy
					|| canApplyPipWindowPolicy(launchMode)
					|| canCalculateBoundsForFullscreenTask);

	if (canApplyBoundsFromActivityOptions) {
		hasInitialBounds = true;
		// |launchMode| at this point can be fullscreen, PIP, MultiWindow, etc. Only set
		// freeform windowing mode if appropriate by checking |canApplyFreeformWindowPolicy|.
		launchMode = launchMode == WINDOWING_MODE_UNDEFINED && canApplyFreeformWindowPolicy
				? WINDOWING_MODE_FREEFORM
				: launchMode;
		outParams.mBounds.set(options.getLaunchBounds());
		if (DEBUG) appendLog("activity-options-bounds=" + outParams.mBounds);
	} else if (canApplyWindowLayout) {
		mTmpBounds.set(currentParams.mBounds);
		getLayoutBounds(suggestedDisplayArea, root, layout, mTmpBounds);
		if (!mTmpBounds.isEmpty()) {
			launchMode = canApplyFreeformWindowPolicy ? WINDOWING_MODE_FREEFORM : launchMode;
			outParams.mBounds.set(mTmpBounds);
			hasInitialBounds = true;
			hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow = true;
			if (DEBUG) appendLog("bounds-from-layout=" + outParams.mBounds);
		} else {
			if (DEBUG) appendLog("empty-window-layout");
		}
	} else if (launchMode == WINDOWING_MODE_MULTI_WINDOW
			&& options != null && options.getLaunchBounds() != null) {
		// TODO: Investigate whether we can migrate this clause to the
		//  |canApplyBoundsFromActivityOptions| case above.
		outParams.mBounds.set(options.getLaunchBounds());
		hasInitialBounds = true;
		if (DEBUG) appendLog("multiwindow-activity-options-bounds=" + outParams.mBounds);
	}

	// STEP 2.2: Check if previous modifier or the controller (referred as "callers" below) has
	// some opinions on launch mode and launch bounds. If they have opinions and there is no
	// initial bounds set in parameters. Note the check on display ID is also input param
	// related because we always defer to callers' suggestion if there is no specific display ID
	// in options or from source activity.
	//
	// If opinions from callers don't need any further resolution, we try to honor that as is as
	// much as possible later.

	// Flag to indicate if current param needs no further resolution. It's true it current
	// param isn't freeform mode, or it already has launch bounds.
	boolean fullyResolvedCurrentParam = false;
	// We inherit launch params from previous modifiers or LaunchParamsController if options,
	// layout and display conditions are not contradictory to their suggestions. It's important
	// to carry over their values because LaunchParamsController doesn't automatically do that.
	// We only check if display matches because display area can be changed later.
	if (!currentParams.isEmpty() && !hasInitialBounds
			&& (currentParams.mPreferredTaskDisplayArea == null
				|| currentParams.mPreferredTaskDisplayArea.getDisplayId()
					== display.getDisplayId())) {
		// Only set windowing mode if display is in freeform. If the display is in fullscreen
		// mode we should only launch a task in fullscreen mode.
		if (currentParams.hasWindowingMode()
				&& suggestedDisplayArea.inFreeformWindowingMode()) {
			launchMode = currentParams.mWindowingMode;
			fullyResolvedCurrentParam = launchMode != WINDOWING_MODE_FREEFORM;
			if (DEBUG) {
				appendLog("inherit-" + WindowConfiguration.windowingModeToString(launchMode));
			}
		}

		if (!currentParams.mBounds.isEmpty()) {
			// Carry over bounds from callers regardless of launch mode because bounds is still
			// used to restore last non-fullscreen bounds when launch mode is not freeform.
			outParams.mBounds.set(currentParams.mBounds);
			fullyResolvedCurrentParam = true;
			if (launchMode == WINDOWING_MODE_FREEFORM) {
				if (DEBUG) appendLog("inherit-bounds=" + outParams.mBounds);
			}
		}
	}

	// STEP 2.3: Adjust launch parameters as needed for freeform display. We enforce the
	// policies related to unresizable apps here. If an app is unresizable and the freeform
	// size-compat mode is enabled, it can be launched in freeform depending on other properties
	// such as orientation. Otherwise, the app is forcefully launched in maximized. The rest of
	// this step is to define the default policy when there is no initial bounds or a fully
	// resolved current params from callers.

	// hasInitialBoundsForSuggestedDisplayAreaInFreeformMode is set if the outParams.mBounds
	// is set with the suggestedDisplayArea. If it is set, but the eventual TaskDisplayArea is
	// different, we should recalcuating the bounds.
	boolean hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = false;
	if (suggestedDisplayArea.inFreeformWindowingMode()) {
		if (launchMode == WINDOWING_MODE_PINNED) {
			if (DEBUG) appendLog("picture-in-picture");
		} else if (!root.isResizeable()) {
			if (shouldLaunchUnresizableAppInFreeform(root, suggestedDisplayArea, options)) {
				launchMode = WINDOWING_MODE_FREEFORM;
				if (outParams.mBounds.isEmpty()) {
					getTaskBounds(root, suggestedDisplayArea, layout, launchMode,
							hasInitialBounds, outParams.mBounds);
					hasInitialBoundsForSuggestedDisplayAreaInFreeformMode = true;
				}
				if (DEBUG) appendLog("unresizable-freeform");
			} else {
				launchMode = WINDOWING_MODE_FULLSCREEN;
				outParams.mBounds.setEmpty();
				if (DEBUG) appendLog("unresizable-forced-maximize");
			}
		}
	} else {
		if (DEBUG) appendLog("non-freeform-task-display-area");
	}

    // 更新WindoingMode,集成TaskDisplayArea的WindowMode
	// If launch mode matches display windowing mode, let it inherit from display.
	outParams.mWindowingMode = launchMode == suggestedDisplayArea.getWindowingMode()
			? WINDOWING_MODE_UNDEFINED : launchMode;

	if (phase == PHASE_WINDOWING_MODE) {
		return RESULT_CONTINUE;
	}

	// STEP 3: Finalize the display area. Here we allow WM shell route all launches that match
	// certain criteria to specific task display areas.
	final int resolvedMode = (launchMode != WINDOWING_MODE_UNDEFINED) ? launchMode
			: suggestedDisplayArea.getWindowingMode();
	TaskDisplayArea taskDisplayArea = suggestedDisplayArea;
	// If launch task display area is set in options we should just use it. We assume the
	// suggestedDisplayArea has the right one in this case.
	if (options == null || (options.getLaunchTaskDisplayArea() == null
			&& options.getLaunchTaskDisplayAreaFeatureId() == FEATURE_UNDEFINED)) {
		final int activityType =
				mSupervisor.mRootWindowContainer.resolveActivityType(root, options, task);
		display.forAllTaskDisplayAreas(displayArea -> {
			final Task launchRoot = displayArea.getLaunchRootTask(
					resolvedMode, activityType, null /* ActivityOptions */,
					null /* sourceTask*/, 0 /* launchFlags */);
			if (launchRoot == null) {
				return false;
			}
			mTmpDisplayArea = displayArea;
			return true;
		});
		// We may need to recalculate the bounds and the windowing mode if the new
		// TaskDisplayArea is different from the suggested one we used to calculate the two
		// configurations.
		if (mTmpDisplayArea != null && mTmpDisplayArea != suggestedDisplayArea) {
			outParams.mWindowingMode = (launchMode == mTmpDisplayArea.getWindowingMode())
					? WINDOWING_MODE_UNDEFINED : launchMode;
			if (hasInitialBoundsForSuggestedDisplayAreaInFreeformWindow) {
				outParams.mBounds.setEmpty();
				getLayoutBounds(mTmpDisplayArea, root, layout, outParams.mBounds);
				hasInitialBounds = !outParams.mBounds.isEmpty();
			} else if (hasInitialBoundsForSuggestedDisplayAreaInFreeformMode) {
				outParams.mBounds.setEmpty();
				getTaskBounds(root, mTmpDisplayArea, layout, launchMode,
						hasInitialBounds, outParams.mBounds);
			}
		}

		if (mTmpDisplayArea != null) {
			taskDisplayArea = mTmpDisplayArea;
			mTmpDisplayArea = null;
			appendLog("overridden-display-area=["
					+ WindowConfiguration.activityTypeToString(activityType) + ", "
					+ WindowConfiguration.windowingModeToString(resolvedMode) + ", "
					+ taskDisplayArea + "]");
		}
	}
	appendLog("display-area=" + taskDisplayArea);
	outParams.mPreferredTaskDisplayArea = taskDisplayArea;

	if (phase == PHASE_DISPLAY_AREA) {
		return RESULT_CONTINUE;
	}

	// STEP 4: Determine final launch bounds based on resolved windowing mode and activity
	// requested orientation. We set bounds to empty for fullscreen mode and keep bounds as is
	// for all other windowing modes that's not freeform mode. One can read comments in
	// relevant methods to further understand this step.
	//
	// We skip making adjustments if the params are fully resolved from previous results.
	if (fullyResolvedCurrentParam) {
		if (resolvedMode == WINDOWING_MODE_FREEFORM) {
			// Make sure bounds are in the displayArea.
			if (currentParams.mPreferredTaskDisplayArea != taskDisplayArea) {
				adjustBoundsToFitInDisplayArea(taskDisplayArea, layout, outParams.mBounds);
			}
			// Even though we want to keep original bounds, we still don't want it to stomp on
			// an existing task.
			adjustBoundsToAvoidConflictInDisplayArea(taskDisplayArea, outParams.mBounds);
		}
	} else {
		if (source != null && source.inFreeformWindowingMode()
				&& resolvedMode == WINDOWING_MODE_FREEFORM
				&& outParams.mBounds.isEmpty()
				&& source.getDisplayArea() == taskDisplayArea) {
			// Set bounds to be not very far from source activity.
			cascadeBounds(source.getConfiguration().windowConfiguration.getBounds(),
					taskDisplayArea, outParams.mBounds);
		}
		getTaskBounds(root, taskDisplayArea, layout, resolvedMode, hasInitialBounds,
				outParams.mBounds);
	}
	return RESULT_CONTINUE;
}

首先,对outParams.mBounds进行了设置

outParams.mWindowingMode = launchMode == suggestedDisplayArea.getWindowingMode()

        ? WINDOWING_MODE_UNDEFINED : launchMode;

则对mWindowingMode进行了设置。这里沿用的是上层TaskDisplayArea的launchMode。

关于TaskDisplayArea的launchMode, 默认值来自DisplayConent的构造方法。在创建DisplayConent时,会调用setWindowingMode(WINDOWING_MODE_FULLSCREEN);

TaskDisplayArea创建Root Task

到目前,如果没有复用的话,mTargetRootTask还是空。所以会通过下面代码来创建新Task

if (mTargetRootTask == null) {
	mTargetRootTask = getOrCreateRootTask(mStartActivity, mLaunchFlags, targetTask,
			mOptions);
}

getOrCreateRootTask会调用mRootWindowContainer.getOrCreateRootTask(r, aOptions, task, sourceTask, onTop, mLaunchParams, launchFlags);来创建Task:

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) {
	final Task candidateRoot = Task.fromWindowContainerToken(options.getLaunchRootTask());
	if (candidateRoot != null && canLaunchOnDisplay(r, candidateRoot)) {
		return candidateRoot;
	}
}
// *** 位置1
// Next preference goes to the task id set in the activity options.
if (options != null) {
	final int candidateTaskId = options.getLaunchTaskId();
	if (candidateTaskId != INVALID_TASK_ID) {
		// Temporarily set the task id to invalid in case in re-entry.
		options.setLaunchTaskId(INVALID_TASK_ID);
		final Task task = anyTaskForId(candidateTaskId,
				MATCH_ATTACHED_TASK_OR_RECENT_TASKS_AND_RESTORE, options, onTop);
		options.setLaunchTaskId(candidateTaskId);
		if (canLaunchOnDisplay(r, task)) {
			return task.getRootTask();
		}
	}
}
/*** 位置2
// Next preference goes to the TaskDisplayArea candidate from launchParams
// or activity options.
TaskDisplayArea taskDisplayArea = null;
if (launchParams != null && launchParams.mPreferredTaskDisplayArea != null) {
	taskDisplayArea = launchParams.mPreferredTaskDisplayArea;
} else if (options != null) {
	final WindowContainerToken daToken = options.getLaunchTaskDisplayArea();
	taskDisplayArea = daToken != null
			? (TaskDisplayArea) WindowContainer.fromBinder(daToken.asBinder()) : null;
	if (taskDisplayArea == null) {
		final int launchDisplayId = options.getLaunchDisplayId();
		if (launchDisplayId != INVALID_DISPLAY) {
			final DisplayContent displayContent = getDisplayContent(launchDisplayId);
			if (displayContent != null) {
				taskDisplayArea = displayContent.getDefaultTaskDisplayArea();
			}
		}
	}
}

// *** 位置3
final int activityType = resolveActivityType(r, options, candidateTask);
// *** 位置4
if (taskDisplayArea != null) {
	if (canLaunchOnDisplay(r, taskDisplayArea.getDisplayId())) {
		/*** 位置5
        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;
if (candidateTask != null) {
	rootTask = candidateTask.getRootTask();
}
if (rootTask == null && r != null) {
	rootTask = r.getRootTask();
}
int windowingMode = launchParams != null ? launchParams.mWindowingMode
		: WindowConfiguration.WINDOWING_MODE_UNDEFINED;
if (rootTask != null) {
	taskDisplayArea = rootTask.getDisplayArea();
	if (taskDisplayArea != null
			&& canLaunchOnDisplay(r, taskDisplayArea.mDisplayContent.mDisplayId)) {
		if (windowingMode == WindowConfiguration.WINDOWING_MODE_UNDEFINED) {
			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.
		if (rootTask.isCompatible(windowingMode, activityType)
				|| rootTask.mCreatedByOrganizer) {
			return rootTask;
		}
	} else {
		taskDisplayArea = null;
	}

}

// Falling back to default task container
if (taskDisplayArea == null) {
	taskDisplayArea = getDefaultTaskDisplayArea();
}
return taskDisplayArea.getOrCreateRootTask(r, options, candidateTask, sourceTask,
		launchParams, launchFlags, activityType, onTop);
}

这里是创建一个RootTask。

首先看位置1,candidateTaskId是空,就不复用了。位置2,launchParams.mPreferredTaskDisplayArea非空,为当前默认的DisplayContent下的TaskDisplayArea,于是taskDisplayArea的到赋值。位置3, activityType返回的是ACTIVITY_TYPE_STANDARD。

platform/frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

int resolveActivityType(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
		@Nullable Task task) {
	// Preference is given to the activity type for the activity then the task since the type
	// once set shouldn't change.
	int activityType = r != null ? r.getActivityType() : ACTIVITY_TYPE_UNDEFINED;
	if (activityType == ACTIVITY_TYPE_UNDEFINED && task != null) {
		activityType = task.getActivityType();
	}
	if (activityType != ACTIVITY_TYPE_UNDEFINED) {
		return activityType;
	}
	if (options != null) {
		activityType = options.getLaunchActivityType();
	}
	return activityType != ACTIVITY_TYPE_UNDEFINED ? activityType : ACTIVITY_TYPE_STANDARD;
}

位置4,canLaunchOnDisplay的检查,最终调用ActivityTaskSupervisor的canPlaceEntityOnDisplay:

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityTaskSupervisor.java

private boolean canPlaceEntityOnDisplay(int displayId, int callingPid, int callingUid,
		Task task, ActivityInfo activityInfo) {
	if (displayId == DEFAULT_DISPLAY) {
		// No restrictions for the default display.
		return true;
	}
	if (!mService.mSupportsMultiDisplay) {
		// Can't launch on secondary displays if feature is not supported.
		return false;
	}

	if (!isCallerAllowedToLaunchOnDisplay(callingPid, callingUid, displayId, activityInfo)) {
		// Can't place activities to a display that has restricted launch rules.
		// In this case the request should be made by explicitly adding target display id and
		// by caller with corresponding permissions. See #isCallerAllowedToLaunchOnDisplay().
		return false;
	}

	final DisplayContent displayContent =
			mRootWindowContainer.getDisplayContentOrCreate(displayId);
	if (displayContent != null) {
		final ArrayList<ActivityInfo> activities = new ArrayList<>();
		if (activityInfo != null) {
			activities.add(activityInfo);
		}
		if (task != null) {
			task.forAllActivities((r) -> {
				activities.add(r.info);
			});
		}
		return displayContent.mDwpcHelper.canContainActivities(activities,
					displayContent.getWindowingMode());
	}

	return true;
}

这里是针对多屏幕情况下,Activity是否可在对应屏幕上启动的合法性检查。如果只有一块屏幕,返回true。

所以,最终会在位置5调用taskDisplayArea.getOrCreateRootTask来创建Root Task:

platform/frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java

Task getOrCreateRootTask(@Nullable ActivityRecord r, @Nullable ActivityOptions options,
		@Nullable Task candidateTask, @Nullable Task sourceTask,
		@Nullable LaunchParams launchParams, int launchFlags, int activityType, boolean onTop) {
	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.
		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 = validateWindowingMode(windowingMode, r, candidateTask);
	return getOrCreateRootTask(windowingMode, activityType, onTop, candidateTask, sourceTask,
			options, launchFlags);
}

/**
 * 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) {
	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.
	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;
		final Task launchParentTask = getLaunchRootTask(resolvedWindowingMode, activityType,
				options, sourceTask, launchFlags, candidateTask);
		if (launchParentTask != null) {
			if (candidateTask.getParent() == null) {
				launchParentTask.addChild(candidateTask, position);
			} else if (candidateTask.getParent() != launchParentTask) {
				candidateTask.reparent(launchParentTask, position);
			}
		} 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.
		if (windowingMode != WINDOWING_MODE_UNDEFINED && candidateTask.isRootTask()
				&& candidateTask.getWindowingMode() != windowingMode) {
			candidateTask.mTransitionController.collect(candidateTask);
			candidateTask.setWindowingMode(windowingMode);
		}
		return candidateTask.getRootTask();
	}
	return new Task.Builder(mAtmService)
			.setWindowingMode(windowingMode)
			.setActivityType(activityType)
			.setOnTop(onTop)
			.setParent(this)
			.setSourceTask(sourceTask)
			.setActivityOptions(options)
			.setLaunchFlags(launchFlags)
			.build();
}

首先我们知道,本文场景下,launchParams.mWindowingMode之前的代码赋值为了WINDOWING_MODE_FULLSCREEN, 因此这里windowingMode为WINDOWING_MODE_FULLSCREEN。所以接下来alwaysCreateRootTask这里:

platform/frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

static boolean alwaysCreateRootTask(int windowingMode, int activityType) {
	// Always create a root task for fullscreen, freeform, and multi windowing
	// modes so that we can manage visual ordering and return types correctly.
	return (activityType == ACTIVITY_TYPE_STANDARD || activityType == ACTIVITY_TYPE_RECENTS)
			&& (windowingMode == WINDOWING_MODE_FULLSCREEN
			|| windowingMode == WINDOWING_MODE_FREEFORM
			|| windowingMode == WINDOWING_MODE_PINNED
			|| windowingMode == WINDOWING_MODE_MULTI_WINDOW);
}

由于前面知道activityType是ACTIVITY_TYPE_STANDARD, 所以本方法返回true。

在我们的场景candidateTask为空,因此会走Task.Builder(mAtmService)进行Task的创建。

看下Task.Builder的build方法


Task build() {
            if (mParent != null && mParent instanceof TaskDisplayArea) {
                validateRootTask((TaskDisplayArea) mParent);
            }

            if (mActivityInfo == null) {
                mActivityInfo = new ActivityInfo();
                mActivityInfo.applicationInfo = new ApplicationInfo();
            }

            mUserId = UserHandle.getUserId(mActivityInfo.applicationInfo.uid);
            mTaskAffiliation = mTaskId;
            mLastTimeMoved = System.currentTimeMillis();
            mNeverRelinquishIdentity = true;
            mCallingUid = mActivityInfo.applicationInfo.uid;
            mCallingPackage = mActivityInfo.packageName;
            mResizeMode = mActivityInfo.resizeMode;
            mSupportsPictureInPicture = mActivityInfo.supportsPictureInPicture();
            if (!mRemoveWithTaskOrganizer && mActivityOptions != null) {
                mRemoveWithTaskOrganizer = mActivityOptions.getRemoveWithTaskOranizer();
            }

            final Task task = buildInner();
            task.mHasBeenVisible = mHasBeenVisible;

            // Set activity type before adding the root task to TaskDisplayArea, so home task can
            // be cached, see TaskDisplayArea#addRootTaskReferenceIfNeeded().
            if (mActivityType != ACTIVITY_TYPE_UNDEFINED) {
                task.setActivityType(mActivityType);
            }

            if (mParent != null) {
                if (mParent instanceof Task) {
                    final Task parentTask = (Task) mParent;
                    parentTask.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM,
                            (mActivityInfo.flags & FLAG_SHOW_FOR_ALL_USERS) != 0);
                } else {
                // ** task加入parent也就是TaskDisplayArea栈顶
                    mParent.addChild(task, mOnTop ? POSITION_TOP : POSITION_BOTTOM);
                }
            }

            // Set windowing mode after attached to display area or it abort silently.
            if (mWindowingMode != WINDOWING_MODE_UNDEFINED) {
                task.setWindowingMode(mWindowingMode, true /* creating */);
            }
            return task;
        }

注意mParent.addChildtask加入TaskDisplayArea栈顶

Activity加入Task

回到startActivityInner。getOrCreateRootTask()创建Task紧接着判断newTask因为前面newTask = targetTask == null targetTast可复用task所以这里newTasktrue。因此紧接着调用setNewTask()注意参数taskToAffiliatenull 因为mSourceRecordnull

platform/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java

private void setNewTask(Task taskToAffiliate) {
        final boolean toTop = !mLaunchTaskBehind && !mAvoidMoveToFront;
         //返回mTargetRootTask自身
        final Task task = mTargetRootTask.reuseOrCreateTask(
                mStartActivity.info, mIntent, mVoiceSession,
                mVoiceInteractor, toTop, mStartActivity, mSourceRecord, mOptions);
        task.mTransitionController.collectExistenceChange(task);
        // newactivityRecord加入mTargetRootTask
        addOrReparentStartingActivity(task, "setTaskFromReuseOrCreateNewTask");

        ProtoLog.v(WM_DEBUG_TASKS, "Starting new activity %s in new task %s",
                mStartActivity, mStartActivity.getTask());

        if (taskToAffiliate != null) {
            mStartActivity.setTaskToAffiliateWith(taskToAffiliate);
        }
    }

关注reuseOrCreateTask()方法, 其参数mStartActivity之前新new的ActivityRecord, mIntent是客户端传过来intent, toTop为true:

platform/frameworks/base/services/core/java/com/android/server/wm/Task.java

Task reuseOrCreateTask(ActivityInfo info, Intent intent, IVoiceInteractionSession voiceSession,
		IVoiceInteractor voiceInteractor, boolean toTop, ActivityRecord activity,
		ActivityRecord source, ActivityOptions options) {

	Task task;
	if (canReuseAsLeafTask()) {
       // 这里
		// This root task will only contain one task, so just return itself since all root
		// tasks ara now tasks and all tasks are now root tasks.
		task = reuseAsLeafTask(voiceSession, voiceInteractor, intent, info, activity);
	} else {
		// Create child task since this root task can contain multiple tasks.
		final int taskId = activity != null
				? mTaskSupervisor.getNextTaskIdForUser(activity.mUserId)
				: mTaskSupervisor.getNextTaskIdForUser();
		final int activityType = getActivityType();
		task = new Task.Builder(mAtmService)
				.setTaskId(taskId)
				.setActivityInfo(info)
				.setActivityOptions(options)
				.setIntent(intent)
				.setVoiceSession(voiceSession)
				.setVoiceInteractor(voiceInteractor)
				.setOnTop(toTop)
				.setParent(this)
				.build();
	}

	int displayId = getDisplayId();
	if (displayId == INVALID_DISPLAY) displayId = DEFAULT_DISPLAY;
	final boolean isLockscreenShown = mAtmService.mTaskSupervisor.getKeyguardController()
			.isKeyguardOrAodShowing(displayId);
	if (!mTaskSupervisor.getLaunchParamsController()
			.layoutTask(task, info.windowLayout, activity, source, options)
			&& !getRequestedOverrideBounds().isEmpty()
			&& task.isResizeable() && !isLockscreenShown) {
		task.setBounds(getRequestedOverrideBounds());
	}

	return task;
}

关注canReuseAsLeafTask()

platform/frameworks/base/services/core/java/com/android/server/wm/Task.java

private boolean canReuseAsLeafTask() {
	// Cannot be reused as leaf task if this task is created by organizer or having child tasks.
	if (mCreatedByOrganizer || !isLeafTask()) {
		return false;
	}

	// Existing Tasks can be reused if a new root task will be created anyway.
	final int windowingMode = getWindowingMode();
	final int activityType = getActivityType();
	return DisplayContent.alwaysCreateRootTask(windowingMode, activityType);
}
platform/frameworks/base/services/core/java/com/android/server/wm/Task.java

boolean isLeafTask() {
	for (int i = mChildren.size() - 1; i >= 0; --i) {
		if (mChildren.get(i).asTask() != null) {
			return false;
		}
	}
	return true;
}

canReuseAsLeafTask()返回true因为isLeafTask()是true(此时还没有mChildren)DisplayContent.alwaysCreateRootTask之前看过返回true

所以会执行:

task = reuseAsLeafTask(voiceSession, voiceInteractor, intent, info, activity);

这句最终返回task自身this

整个setNewTaskfinal Task task = mTargetRootTask.reuseOrCreateTask 可以认为返回mTargetRootTask自身随后调用addOrReparentStartingActivitynewmStartActivity加入newmTargetRootTask

platform/frameworks/base/services/core/java/com/android/server/wm/Task.java

private void addOrReparentStartingActivity(@NonNull Task task, String reason) {
	TaskFragment newParent = task;
	if (mInTaskFragment != null) {
		int embeddingCheckResult = canEmbedActivity(mInTaskFragment, mStartActivity, task);
		if (embeddingCheckResult == EMBEDDING_ALLOWED) {
			newParent = mInTaskFragment;
			mStartActivity.mRequestedLaunchingTaskFragmentToken =
					mInTaskFragment.getFragmentToken();
		} else {
			// Start mStartActivity to task instead if it can't be embedded to mInTaskFragment.
			sendCanNotEmbedActivityError(mInTaskFragment, embeddingCheckResult);
		}
	} else {
		TaskFragment candidateTf = mAddingToTaskFragment != null ? mAddingToTaskFragment : null;
		if (candidateTf == null) {
			// Puts the activity on the top-most non-isolated navigation TF, unless the
			// activity is launched from the same TF.
			final TaskFragment sourceTaskFragment =
					mSourceRecord != null ? mSourceRecord.getTaskFragment() : null;
			final ActivityRecord top = task.getActivity(-> {
				if (!r.canBeTopRunning()) {
					return false;
				}
				final TaskFragment taskFragment = r.getTaskFragment();
				return !taskFragment.isIsolatedNav() || (sourceTaskFragment != null
						&& sourceTaskFragment == taskFragment);
			});
			if (top != null) {
				candidateTf = top.getTaskFragment();
			}
		}
		if (candidateTf != null && candidateTf.isEmbedded()
				&& canEmbedActivity(candidateTf, mStartActivity, task) == EMBEDDING_ALLOWED) {
			// Use the embedded TaskFragment of the top activity as the new parent if the
			// activity can be embedded.
			newParent = candidateTf;
		}
	}
	if (mStartActivity.getTaskFragment() == null
			|| mStartActivity.getTaskFragment() == newParent) {
		newParent.addChild(mStartActivity, POSITION_TOP);
	} else {
		mStartActivity.reparent(newParent, newParent.getChildCount() /* top */, reason);
	}
}

此处TaskFragment newParent = taskgetTaskFragmentnullnewParent通过newParent.addChild(mStartActivity, POSITION_TOP)activity加入task

启动Activity

回到startActivityInner,来到位置4。由于上面的mTargetRootTask.startActivityLocked对整体流向没有影响,暂不讨论。下面看mDoResume条件满足内的逻辑:if (!mTargetRootTask.isTopActivityFocusable() ...。

platform/frameworks/base/services/core/java/com/android/server/wm/TaskFragment.java

boolean isTopActivityFocusable() {
	final ActivityRecord r = topRunningActivity();
	return r != null ? r.isFocusable()
			: (isFocusable() && getWindowConfiguration().canReceiveKeys());
}

由于mTargetRootTask此时是刚创建的,所以topRunningActivity所以这里task本身isFocusable()

platform/frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java

boolean isFocusable() {
	final WindowContainer parent = getParent();
	return (parent == null || parent.isFocusable()) && mIsFocusable;
}

由于mTargetRootTask自身的mIsFocusable属性默认trueparent TaskDisplayArea的isFocusable也是true因此整体返回true

那么startActivityInner接下就会else分支mRootWindowContainer.resumeFocusedTasksTopActivities(

这里activity启动

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值