上篇梳理到:
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.addChild将本task加入到了TaskDisplayArea的栈顶。
将Activity加入到Task
回到startActivityInner。getOrCreateRootTask()创建完Task后,紧接着判断newTask。因为前面newTask = targetTask == null, targetTast是指可复用的task,为空,所以这里newTask为true。因此紧接着调用setNewTask()。注意参数taskToAffiliate是null, 因为mSourceRecord是null。
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);
// 将新new的activityRecord加入到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。
整个setNewTask里的final Task task = mTargetRootTask.reuseOrCreateTask, 可以认为返回了mTargetRootTask自身,随后调用addOrReparentStartingActivity,把新new的mStartActivity加入到新new的mTargetRootTask
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(r -> {
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 = task,getTaskFragment为null,newParent通过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属性默认是true,其parent TaskDisplayArea的isFocusable也是true,因此整体返回true。
那么startActivityInner里接下来就会走else分支里的mRootWindowContainer.resumeFocusedTasksTopActivities(
这里,activity将被启动。