Android14 - AMS之Activity启动过程(1)-CSDN博客
Android14 - AMS之Activity启动过程(3)-CSDN博客
上篇梳理到:
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的栈顶。
将 Activi ty 加入 到 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将被启动。