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

news2024/11/25 10:32:08
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.addChildtask加入TaskDisplayArea栈顶

Activity加入Task

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

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

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

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

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

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

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

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

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

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

	return task;
}

关注canReuseAsLeafTask()

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

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

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

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

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

所以会执行:

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

这句最终返回task自身this

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

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

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

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

启动Activity

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

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

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

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

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

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

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

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

这里activity启动

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1531355.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件系统开发设计的基本流程

一、前言 经过年的工程实践软件系统开发的流程演变有很多种&#xff0c;但是最基本的还是瀑布模型。但是由于近几年演变了很多种模型&#xff0c;现在很多公司的研发流程并不遵循瀑布模型。主要原因是无法满足市场竞争的需求。比如在哪某个节日需要敏捷上线活动等这样的场景。没…

python网络爬虫实战教学——urllib的使用(1)

文章目录 专栏导读1、前言2、urllib的使用3、发送请求3.1 urlopen3.2 request 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫实战教学》&#xff0c;本专栏针对…

支付宝小程序一次性订阅requestSubscribeMessage授权和操作详解

一、授权 — requestSubscribeMessage my.requestSubscribeMessage({entityIds: [xxxx],success: (res) > {console.log("success回调", res)},fail: res > {console.log(fail回调, res)} })success 回调函数 behavior String 用户订阅操作结果 — subscribe …

【译】矢量数据库 101 - 什么是矢量数据库?

原文地址&#xff1a;Vector Database 101 - What is a Vector Database? 1. 简介 大家好——欢迎回到 Milvus 教程。在上一教程中&#xff0c;我们快速浏览了每天产生的日益增长的数据量。然后&#xff0c;我们介绍了如何将这些数据分成结构化/半结构化数据和非结构化数据&…

【python】Matplotlib库安装教程

1.你要有python&#xff08;如果没装可以看这篇文章文章安装&#xff09; python及pycharm安装教程&#xff08;2024超详细&#xff09; 2.更新pip&#xff08;此步可跳过&#xff09; win R &#xff1b;输入cmd&#xff08;就是打开命令提示符&#xff09; 打开后&#x…

【Linux】传输层协议:TCP/UDP

目录 netstat pidof UDP协议 TCP协议 TCP协议段格式 TCP协议的相关机制 确认应答&#xff08;ACK&#xff09;机制 超时重传机制 连接管理机制 服务端状态转换 客户端状态转化 流量控制 流量控制常见问题&#xff1a; 滑动窗口 拥塞控制 延迟应答 面向字节流…

electron-builder 打包问题,下载慢解决方案

目录 问题说明设置下载源 &#xff1f;解决方案思路下载Electron下载winCodeSign下载nsis下载nsis-resources 总结 问题说明 项目使用了Electron&#xff0c;在第一次打包时会遇见下载慢&#xff0c;导致打包进度几乎停滞不前&#xff0c;甚至可能直接报错 其实这是因为Electr…

UML学习体会

1. 水在前面 本来写作的水平就很一般&#xff0c;平时写的也少。最近看到一些文章说学习最好的方式是输出&#xff0c;刚好又重温了一遍UML方面的基础&#xff0c;所以想记录点学习心得。而且说实话这玩意平时基本不怎么用&#xff08;偶尔倒是看看别人的成果&#xff09;&…

智能客服知识库如何搭建比较方便?教程奉上!

随着科技的进步&#xff0c;人工智能已深入到我们日常生活的各个角落。在客服行业里&#xff0c;智能客服知识库的出现&#xff0c;不仅极大地减轻了客服人员的工作负担&#xff0c;还提升了用户的服务体验。那么&#xff0c;怎样才能建立一个方便和实用的智能客服知识库呢&…

Acwing.167 木棒(回溯)

题目 乔治拿来一组等长的木棒&#xff0c;将它们随机地砍断&#xff0c;使得每一节木棍的长度都不超过 50 个长度单位。 然后他又想把这些木棍恢复到为裁截前的状态&#xff0c;但忘记了初始时有多少木棒以及木棒的初始长度。 请你设计一个程序&#xff0c;帮助乔治计算木棒…

【马斯克开源GROK-1模型】意味着什么?

目录 1.激动人心的消息 Grok-1 根据 Apache License 2.0 开放源代码 题外话-介绍JAX框架 2.grok-1模型参数介绍 3.推理grok-1模型需要多大显存&#xff1f; 4.grok-1开源意味着什么&#xff1f; 5.最后&#xff0c;让我们一同期待开源grok-1模型的训练代码&#xff01;…

电脑内存取证

目录 电脑内存文件与volatility工具获取电脑用户名获取程序最后一次运行的时间总结 电脑内存文件与volatility工具 这个文件就是内存文件 提取内存信息&#xff0c;我们一般采用volatility工具进行提取&#xff0c;具体如何安装和使用&#xff0c;直接去网上搜索就可以了 获取…

力扣Lc18--- 168. Excel表列名称(java版)-2024年3月19日

1.题目描述 2.知识点 因为是输入字符 然后显示数字。 就类似2进制10代表2&#xff0c;110代表4&#xff0c;用某进制次幂的方式返回最后的数字结果。 3.代码实现 class Solution {public int titleToNumber(String columnTitle) {int sum0;for(int i0;i<columnTitle.len…

大数据架构技术选型

OLAP数据库选型对比&#xff1a; AnalyticDB(阿里&#xff09;、Hologres&#xff08;阿里&#xff09;、Doris、StarRocks、ClickHouse、Hbase AnalyticDB MySQL 技术架构 Adb是融合数据库、大数据技术于一体的云原生企业级数据仓库服务、支持高吞吐的数据实时增删改查低延时…

企业合规视角下的恒大地产财务造假案深度剖析

当幕布被拉开&#xff0c;真相暴露在聚光灯下&#xff0c;近日中国房地产市场的巨头恒大地产因财务造假被中国证监会严厉处罚&#xff0c;引起了公众和市场的强烈震动。这份重量级的罚单&#xff0c;不仅以41.75亿元的天价罚款单给恒大地产记下了沉重的一笔&#xff0c;更是将包…

数据仓库相关概述

数据仓库概述 数据仓库概念 数据仓库是一个为数据分析而设计的企业级数据管理系统。数据仓库可集中、整合多个信息源的大量数据&#xff0c;借助数据仓库的分析能力&#xff0c;企业可从数据中获得宝贵的信息进而改进决策。同时&#xff0c;随着时间的推移&#xff0c;数据仓…

IDEA Git恢复DropCommit删除的提交

刚刚Dorp commit了&#xff0c;本地代码也被删除了&#xff0c;如何恢复呢&#xff0c; 从项目中登录git&#xff0c;找到刚刚的commit代码&#xff0c;如下所示&#xff1a;输入命令git reflog 复制代码&#xff0c;到idea中&#xff0c;打开GIt&#xff0c;找到RESET HEAD, …

【数据可视化】使用Python + Gephi,构建中医方剂关系网络图!

代码和示例数据下载 前言 在这篇文章中&#xff0c;我们将会可视化 《七版方剂学》 的药材的关系&#xff0c;我们将使用Python制作节点和边的数据&#xff0c;然后在Gephi中绘制出方剂的网络图。 Gephi是一个专门用于构建网络图的工具&#xff0c;只要你能提供节点和边的数…

Android StateLayout状态页

文章目录 Android StateLayout状态页概述源码使用源码下载 Android StateLayout状态页 概述 StateLayout&#xff08;状态页&#xff09;包含&#xff1a;加载中页面&#xff0c;错误页面&#xff0c;空页面&#xff0c;内含状态默认页面&#xff0c;支持自定义页面。 源码 …

如何在SpringCloud2023中快速集成注册中心

你好&#xff0c;这里是codetrend专栏“SpringCloud2023实战”。欢迎点击关注查看往期文章。 注册中心在前文提到有很多选型&#xff0c;在这里以Spring Cloud Zookeeper为例说明注册中心的集成和使用。 选择Spring Cloud Zookeeper作为注册中心原因如下&#xff1a; 依赖更少…