RootWindowContainer ---> DisplayContent ---> TaskDisplayArea ---> ActivityStack ---> Task ---> ActivityRecord
RootWindowContainer
根容器会处理Window布局,管理Activity相关的Stack和TaskDisplayArea
/** Root {@link WindowContainer} for the device. */
class RootWindowContainer extends WindowContainer
implements DisplayManager.DisplayListener {
// Map from the PID to the top most app which has a focused window of the process.
final HashMap mTopFocusedAppByProcess = new HashMap<>();//updateFocusedWindowLocked
ActivityTaskManagerService mService;
ActivityStackSupervisor mStackSupervisor;
WindowManagerService mWindowManager;
/** Reference to default display so we can quickly look it up. */
private DisplayContent mDefaultDisplay;
updateFocusedWindowLocked
1 mTopFocusedAppByProcess.clear()
2 对每一个子DisplayContent调用updateFocusedWindowLocked更新FocusWindow
3 获取子DisplayContent的mCurrentFocus,将其加入到mTopFocusedAppByProcess
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
mTopFocusedAppByProcess.clear();
boolean changed = false;
int topFocusedDisplayId = INVALID_DISPLAY;
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
final WindowState newFocus = dc.mCurrentFocus;
if (newFocus != null) {
final int pidOfNewFocus = newFocus.mSession.mPid;
if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
}
hasPendingLayoutChanges和copyAnimToLayoutParams
会检查子DisplayContent的pendingChanges是否已经完成壁纸的布局,如果没有完成,将相关动画的mBulkUpdateParams设置为重新布局壁纸
boolean hasPendingLayoutChanges(WindowAnimator animator) {
boolean hasChanges = false;
final int count = mChildren.size();
for (int i = 0; i < count; ++i) {
final int pendingChanges = mChildren.get(i).pendingLayoutChanges;
if ((pendingChanges & FINISH_LAYOUT_REDO_WALLPAPER) != 0) {
animator.mBulkUpdateParams |= SET_WALLPAPER_ACTION_PENDING;
}
if (pendingChanges != 0) {
hasChanges = true;
}
}
return hasChanges;
}
将动画的参数同步到布局中,并判断是否需要重新布局
bulkUpdateParams & SET_UPDATE_ROTATION 旋转方向改变
bulkUpdateParams & SET_WALLPAPER_ACTION_PENDING 壁纸需要重新布局
boolean copyAnimToLayoutParams() {
boolean doRequest = false;
final int bulkUpdateParams = mWmService.mAnimator.mBulkUpdateParams;
if ((bulkUpdateParams & SET_UPDATE_ROTATION) != 0) {
mUpdateRotation = true;
doRequest = true;
}
if ((bulkUpdateParams & SET_ORIENTATION_CHANGE_COMPLETE) == 0) {
mOrientationChangeComplete = false;
} else {
mOrientationChangeComplete = true;
mLastWindowFreezeSource = mWmService.mAnimator.mLastWindowFreezeSource;
if (mWmService.mWindowsFreezingScreen != WINDOWS_FREEZING_SCREENS_NONE) {
doRequest = true;
}
}
if ((bulkUpdateParams & SET_WALLPAPER_ACTION_PENDING) != 0) {
mWallpaperActionPending = true;
}
return doRequest;
}
performSurfacePlacementNoTrace
window布局
mWmService.openSurfaceTransaction();
try {
applySurfaceChangesTransaction();
} catch (RuntimeException e) {
Slog.wtf(TAG, "Unhandled exception in Window Manager", e);
} finally {
mWmService.closeSurfaceTransaction("performLayoutAndPlaceSurfaces");
resumeFocusedStacksTopActivities
Activity切换
targetStack不等于null,且targetStack是TopStackInDisplayArea或者当前TopDisplayFocusedStack为targetStack,直接调用targetStack的resumeTopActivityUncheckedLocked方法,resume最上面的Activity。
stack.topRunningActivity()是调用父类Task的方法
从上往下遍历,如果getFocusedStack不为null,调用resumeTopActivityUncheckedLocked
如果focusedStack为null,且targetStack也为null,则启动resumeHomeActivity
final ActivityStack focusedStack = display.getFocusedStack();
if (focusedStack != null) {
result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
} else if (targetStack == null) {
result |= resumeHomeActivity(null /* prev */, "no-focusable-task",
display.getDefaultTaskDisplayArea());
}
boolean resumeFocusedStacksTopActivities() {
return resumeFocusedStacksTopActivities(null, null, null);
}
boolean resumeFocusedStacksTopActivities(
ActivityStack targetStack, ActivityRecord target, ActivityOptions targetOptions) {
if (!mStackSupervisor.readyToResume()) {
return false;
}
boolean result = false;
if (targetStack != null && (targetStack.isTopStackInDisplayArea()
|| getTopDisplayFocusedStack() == targetStack)) {
result = targetStack.resumeTopActivityUncheckedLocked(target, targetOptions);
}
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
boolean resumedOnDisplay = false;
final DisplayContent display = getChildAt(displayNdx);
for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
final ActivityRecord topRunningActivity = stack.topRunningActivity();
if (!stack.isFocusableAndVisible() || topRunningActivity == null) {
continue;
}
if (stack == targetStack) {
// Simply update the result for targetStack because the targetStack had
// already resumed in above. We don't want to resume it again, especially in
// some cases, it would cause a second launch failure if app process was
// dead.
resumedOnDisplay |= result;
continue;
}
if (taskDisplayArea.isTopStack(stack) && topRunningActivity.isState(RESUMED)) {
// Kick off any lingering app transitions form the MoveTaskToFront
// operation, but only consider the top task and stack on that display.
stack.executeAppTransition(targetOptions);
} else {
resumedOnDisplay |= topRunningActivity.makeActiveIfNeeded(target);
}
}
}
if (!resumedOnDisplay) {
// In cases when there are no valid activities (e.g. device just booted or launcher
// crashed) it's possible that nothing was resumed on a display. Requesting resume
// of top activity in focused stack explicitly will make sure that at least home
// activity is started and resumed, and no recursion occurs.
final ActivityStack focusedStack = display.getFocusedStack();
if (focusedStack != null) {
result |= focusedStack.resumeTopActivityUncheckedLocked(target, targetOptions);
} else if (targetStack == null) {
result |= resumeHomeActivity(null /* prev */, "no-focusable-task",
display.getDefaultTaskDisplayArea());
}
}
}
return result;
}
moveTopStackActivityToPinnedStack
管理一些特殊的StackActivity
moveStackToDisplay
moveStackToTaskDisplayArea
moveActivityToPinnedStack
r.setWindowingMode(intermediateWindowingMode);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
void moveActivityToPinnedStack(ActivityRecord r, String reason) {
mService.deferWindowLayout();
final TaskDisplayArea taskDisplayArea = r.getDisplayArea();
try {
final Task task = r.getTask();
final ActivityStack pinnedStack = taskDisplayArea.getRootPinnedTask();
// This will change the pinned stack's windowing mode to its original mode, ensuring
// we only have one stack that is in pinned mode.
if (pinnedStack != null) {
pinnedStack.dismissPip();
}
// Set a transition to ensure that we don't immediately try and update the visibility
// of the activity entering PIP
r.getDisplayContent().prepareAppTransition(TRANSIT_NONE, false);
final boolean singleActivity = task.getChildCount() == 1;
final ActivityStack stack;
if (singleActivity) {
stack = (ActivityStack) task;
} else {
// In the case of multiple activities, we will create a new task for it and then
// move the PIP activity into the task.
stack = taskDisplayArea.createStack(WINDOWING_MODE_UNDEFINED, r.getActivityType(),
ON_TOP, r.info, r.intent, false /* createdByOrganizer */);
// It's possible the task entering PIP is in freeform, so save the last
// non-fullscreen bounds. Then when this new PIP task exits PIP, it can restore
// to its previous freeform bounds.
stack.setLastNonFullscreenBounds(task.mLastNonFullscreenBounds);
// There are multiple activities in the task and moving the top activity should
// reveal/leave the other activities in their original task.
// On the other hand, ActivityRecord#onParentChanged takes care of setting the
// up-to-dated pinned stack information on this newly created stack.
r.reparent(stack, MAX_VALUE, reason);
}
// The intermediate windowing mode to be set on the ActivityRecord later.
// This needs to happen before the re-parenting, otherwise we will always set the
// ActivityRecord to be fullscreen.
final int intermediateWindowingMode = stack.getWindowingMode();
if (stack.getParent() != taskDisplayArea) {
// stack is nested, but pinned tasks need to be direct children of their
// display area, so reparent.
stack.reparent(taskDisplayArea, true /* onTop */);
}
// Defer the windowing mode change until after the transition to prevent the activity
// from doing work and changing the activity visuals while animating
// TODO(task-org): Figure-out more structured way to do this long term.
r.setWindowingMode(intermediateWindowingMode);
stack.setWindowingMode(WINDOWING_MODE_PINNED);
// Reset the state that indicates it can enter PiP while pausing after we've moved it
// to the pinned stack
r.supportsEnterPipOnTaskSwitch = false;
} finally {
mService.continueWindowLayout();
}
ensureActivitiesVisible(null, 0, false /* preserveWindows */);
resumeFocusedStacksTopActivities();
mService.getTaskChangeNotificationController().notifyActivityPinned(r);
}
一些简易接口
getTopVisibleActivities
getTopDisplayFocusedStack
getTopResumedActivity
getWindowsByName
topRunningActivity
allPausedActivitiesComplete
r.nowVisible有一个条件不满足,return false
!r.isState(PAUSED, STOPPED, STOPPING, FINISHING)有一个条件不满足,return false
boolean allResumedActivitiesVisible() {
boolean foundResumed = false;
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
final ActivityRecord r = stack.getResumedActivity();
if (r != null) {
if (!r.nowVisible) {
return false;
}
foundResumed = true;
}
}
}
}
return foundResumed;
}
boolean allPausedActivitiesComplete() {
boolean pausing = true;
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
for (int sNdx = taskDisplayArea.getStackCount() - 1; sNdx >= 0; --sNdx) {
final ActivityStack stack = taskDisplayArea.getStackAt(sNdx);
final ActivityRecord r = stack.mPausingActivity;
if (r != null && !r.isState(PAUSED, STOPPED, STOPPING, FINISHING)) {
if (DEBUG_STATES) {
Slog.d(TAG_STATES, "allPausedActivitiesComplete: r=" + r
+ " state=" + r.getState());
pausing = false;
} else {
return false;
}
}
}
}
}
return pausing;
}