ActivityRecord的可见性处理过程
ActivityRecord是Activity在SystemService中的实现。
ActivityRecord同步着Activity的生命周期,记录了Activity的关键信息。
ActivityRecord参与了窗口显示、尺寸、图层等很多在SystemService的逻辑操作,是应用在SystemService最重要的基本单元之一
frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
/**
* Make sure that all activities that need to be visible in the system actually are and update
* their configuration.
*/
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows) {
ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
}
/**
* @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
*/
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
if (mStackSupervisor.inActivityVisibilityUpdate()) { //注释1
// Don't do recursive work.
return;
}
try {
mStackSupervisor.beginActivityVisibilityUpdate();
// First the front stacks. In case any are not fullscreen and are in front of home.
for (int displayNdx = getChildCount() - 1; displayNdx >= 0; --displayNdx) {
final DisplayContent display = getChildAt(displayNdx);
display.ensureActivitiesVisible(starting, configChanges, preserveWindows,
notifyClients);
}
} finally {
mStackSupervisor.endActivityVisibilityUpdate();
}
}
ActivityStackSupervisor用于记录和实时更新Activty可见性更新的运行状态,注释1当处于更新时直接返回。首先以DisplayContent为基准开始更新确认。
frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
if (mInEnsureActivitiesVisible) {
// Don't do recursive work.
return;
}
mInEnsureActivitiesVisible = true;
try {
for (int i = getTaskDisplayAreaCount() - 1; i >= 0; --i) {
getTaskDisplayAreaAt(i).ensureActivitiesVisible(starting, configChanges,
preserveWindows, notifyClients);
}
} finally {
mInEnsureActivitiesVisible = false;
}
}
同RootWindowContainer一样,DisplayContent会以TaskDisplayArea为基准开始更新确认。
frameworks/base/services/core/java/com/android/server/wm/TaskDisplayArea.java
void ensureActivitiesVisible(ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
mAtmService.mStackSupervisor.beginActivityVisibilityUpdate();
try {
for (int stackNdx = getStackCount() - 1; stackNdx >= 0; --stackNdx) {
final ActivityStack stack = getStackAt(stackNdx);
stack.ensureActivitiesVisible(starting, configChanges, preserveWindows,
notifyClients);
}
} finally {
mAtmService.mStackSupervisor.endActivityVisibilityUpdate();
}
}
@VisibleForTesting
ActivityStack getTopStack() {
final int count = getChildCount();
return count > 0 ? getChildAt(count - 1) : null;
}
TaskDisplayArea会以ActivityStack为基准开始更新确认。TaskDisplayArea的子成员中TopStack是Chlid中最后的一个。
frameworks/base/services/core/java/com/android/server/wm/ActivityStack.java
/**
* Make sure that all activities that need to be visible in the stack (that is, they
* currently can be seen by the user) actually are and update their configuration.
* @param starting The top most activity in the task.
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param preserveWindows Flag indicating whether windows should be preserved when updating
* configuration in {@link mEnsureActivitiesVisibleHelper}.
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen as part of
* {@link mEnsureActivitiesVisibleHelper}.
*
*/
void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
boolean preserveWindows) {
ensureActivitiesVisible(starting, configChanges, preserveWindows, true /* notifyClients */);
}
/**
* Ensure visibility with an option to also update the configuration of visible activities.
* @see #ensureActivitiesVisible(ActivityRecord, int, boolean)
* @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
* @param starting The top most activity in the task.
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
* @param notifyClients Flag indicating whether the visibility updates should be sent to the
* clients in {@link mEnsureActivitiesVisibleHelper}.
* @param preserveWindows Flag indicating whether windows should be preserved when updating
* configuration in {@link mEnsureActivitiesVisibleHelper}.
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen as part of
* {@link mEnsureActivitiesVisibleHelper}.
*/
// TODO: Should be re-worked based on the fact that each task as a stack in most cases.
void ensureActivitiesVisible(@Nullable ActivityRecord starting, int configChanges,
boolean preserveWindows, boolean notifyClients) {
mTopActivityOccludesKeyguard = false;
mTopDismissingKeyguardActivity = null;
mStackSupervisor.beginActivityVisibilityUpdate();
try {
mEnsureActivitiesVisibleHelper.process(
starting, configChanges, preserveWindows, notifyClients);
if (mTranslucentActivityWaiting != null &&
mUndrawnActivitiesBelowTopTranslucent.isEmpty()) {
// Nothing is getting drawn or everything was already visible, don't wait for timeout.
notifyActivityDrawnLocked(null);
}
} finally {
mStackSupervisor.endActivityVisibilityUpdate();
}
}
从以上代码看,此处就是真正公共的地方了,而逻辑的实现则在EnsureActivitiesVisibleHelper中,EnsureActivitiesVisibleHelper确保activities对于容器处于正确的可见状态,具体工作下面分析。
frameworks/base/services/core/java/com/android/server/wm/EnsureActivitiesVisibleHelper.java
/**
* Ensure visibility with an option to also update the configuration of visible activities.
* @see ActivityStack#ensureActivitiesVisible(ActivityRecord, int, boolean)
* @see RootWindowContainer#ensureActivitiesVisible(ActivityRecord, int, boolean)
* @param starting The top most activity in the task.
* The activity is either starting or resuming.
* Caller should ensure starting activity is visible.
*
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen.
* @param preserveWindows Flag indicating whether windows should be preserved when updating.
* @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
* be sent to the clients.
*/
void process(@Nullable ActivityRecord starting, int configChanges, boolean preserveWindows,
boolean notifyClients) {
Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible start ********" + mContiner,new Exception(TAG_VISIBILITY));
reset(starting, configChanges, preserveWindows, notifyClients);
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "ensureActivitiesVisible behind " + mTop
+ " configChanges=0x" + Integer.toHexString(configChanges));
if (mTop != null) {
mContiner.checkTranslucentActivityWaiting(mTop);
}
// We should not resume activities that being launched behind because these
// activities are actually behind other fullscreen activities, but still required
// to be visible (such as performing Recents animation).
final boolean resumeTopActivity = mTop != null && !mTop.mLaunchTaskBehind
&& mContiner.isTopActivityFocusable()
&& (starting == null || !starting.isDescendantOf(mContiner));
final PooledConsumer f = PooledLambda.obtainConsumer(
EnsureActivitiesVisibleHelper::setActivityVisibilityState, this,
PooledLambda.__(ActivityRecord.class), starting, resumeTopActivity);
mContiner.forAllActivities(f);
f.recycle();
}
private void setActivityVisibilityState(ActivityRecord r, ActivityRecord starting,
final boolean resumeTopActivity) {
final boolean isTop = r == mTop;
if (mAboveTop && !isTop) {
return;
}
mAboveTop = false;
final boolean reallyVisible = r.shouldBeVisible(
mBehindFullscreenActivity, false /* ignoringKeyguard */);
// Check whether activity should be visible without Keyguard influence
if (r.visibleIgnoringKeyguard) {
if (r.occludesParent()) {
// At this point, nothing else needs to be shown in this task.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+ " stackVisible=" + mContainerShouldBeVisible
+ " behindFullscreen=" + mBehindFullscreenActivity);
mBehindFullscreenActivity = true;
} else {
mBehindFullscreenActivity = false;
}
}
if (reallyVisible) {
if (r.finishing) {
return;
}
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make visible? " + r
+ " finishing=" + r.finishing + " state=" + r.getState());
// First: if this is not the current activity being started, make
// sure it matches the current configuration.
if (r != mStarting && mNotifyClients) {
r.ensureActivityConfiguration(0 /* globalChanges */, mPreserveWindows,
true /* ignoreVisibility */);
}
if (!r.attachedToProcess()) {
makeVisibleAndRestartIfNeeded(mStarting, mConfigChanges, isTop,
resumeTopActivity && isTop, r);
} else if (r.mVisibleRequested) {
// If this activity is already visible, then there is nothing to do here.
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY,
"Skipping: already visible at " + r);
if (r.mClientVisibilityDeferred && mNotifyClients) {
r.makeActiveIfNeeded(r.mClientVisibilityDeferred ? null : starting);
r.mClientVisibilityDeferred = false;
}
r.handleAlreadyVisible();
if (mNotifyClients) {
r.makeActiveIfNeeded(mStarting);
}
} else {
r.makeVisibleIfNeeded(mStarting, mNotifyClients);
}
// Aggregate current change flags.
mConfigChanges |= r.configChangeFlags;
} else {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Make invisible? " + r
+ " finishing=" + r.finishing + " state=" + r.getState()
+ " stackShouldBeVisible=" + mContainerShouldBeVisible
+ " behindFullscreenActivity=" + mBehindFullscreenActivity
+ " mLaunchTaskBehind=" + r.mLaunchTaskBehind);
r.makeInvisible();
}
final int windowingMode = mContiner.getWindowingMode();
if (windowingMode == WINDOWING_MODE_FREEFORM) {
// The visibility of tasks and the activities they contain in freeform stack are
// determined individually unlike other stacks where the visibility or fullscreen
// status of an activity in a previous task affects other.
mBehindFullscreenActivity = !mContainerShouldBeVisible;
} else if (!mBehindFullscreenActivity && mContiner.isActivityTypeHome()
&& r.isRootOfTask()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Home task: at " + mContiner
+ " stackShouldBeVisible=" + mContainerShouldBeVisible
+ " behindFullscreenActivity=" + mBehindFullscreenActivity);
// No other task in the home stack should be visible behind the home activity.
// Home activities is usually a translucent activity with the wallpaper behind
// them. However, when they don't have the wallpaper behind them, we want to
// show activities in the next application stack behind them vs. another
// task in the home stack like recents.
mBehindFullscreenActivity = true;
}
}
EnsureActivitiesVisibleHelper对ActivityRecord可见性处理的最重要的方法为setActivityVisibilityState,其中包括可见性的确定以及处理ActivityRecord的过程,本次就对处理ActivityRecord的可见性不做分析了,主要是对可见性的确定做下追踪。
setActivityVisibilityState会通过ActivityRecord的shouldBeVisible方法来获取其可见性,其传递的主要参数为mBehindFullscreenActivity。
mBehindFullscreenActivity此处是通过以下获取的:
/**
* Update all attributes except {@link mContiner} to use in subsequent calculations.
*
* @param starting The activity that is being started
* @param configChanges Parts of the configuration that changed for this activity for evaluating
* if the screen should be frozen.
* @param preserveWindows Flag indicating whether windows should be preserved when updating.
* @param notifyClients Flag indicating whether the configuration and visibility changes shoulc
* be sent to the clients.
*/
void reset(ActivityRecord starting, int configChanges, boolean preserveWindows,
boolean notifyClients) {
mStarting = starting;
mTop = mContiner.topRunningActivity();
// If the top activity is not fullscreen, then we need to make sure any activities under it
// are now visible.
mAboveTop = mTop != null;
mContainerShouldBeVisible = mContiner.shouldBeVisible(mStarting);
mBehindFullscreenActivity = !mContainerShouldBeVisible;
mConfigChanges = configChanges;
mPreserveWindows = preserveWindows;
mNotifyClients = notifyClients;
}
如上代码可知,mBehindFullscreenActivity是在EnsureActivitiesVisibleHelper.process中进行reset时获取的,其值为!mContiner.shouldBeVisible(mStarting)。
frameworks/base/services/core/java/com/android/server/wm/Task.java
/**
* Returns true if the task should be visible.
*
* @param starting The currently starting activity or null if there is none.
*/
boolean shouldBeVisible(ActivityRecord starting) {
return getVisibility(starting) != STACK_VISIBILITY_INVISIBLE;
}
/**
* Returns true if the task should be visible.
*
* @param starting The currently starting activity or null if there is none.
*/
@ActivityStack.StackVisibility
int getVisibility(ActivityRecord starting) {
if (!isAttached() || isForceHidden()) {
return STACK_VISIBILITY_INVISIBLE;
}
boolean gotSplitScreenStack = false;
boolean gotOpaqueSplitScreenPrimary = false;
boolean gotOpaqueSplitScreenSecondary = false;
boolean gotTranslucentFullscreen = false;
boolean gotTranslucentSplitScreenPrimary = false;
boolean gotTranslucentSplitScreenSecondary = false;
boolean shouldBeVisible = true;
// This stack is only considered visible if all its parent stacks are considered visible,
// so check the visibility of all ancestor stacks first.
final WindowContainer parent = getParent();
if (parent.asTask() != null) {
final int parentVisibility = parent.asTask().getVisibility(starting);
if (parentVisibility == STACK_VISIBILITY_INVISIBLE) {
// Can't be visible if parent isn't visible
return STACK_VISIBILITY_INVISIBLE;
} else if (parentVisibility == STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT) {
// Parent is behind a translucent container so the highest visibility this container
// can get is that.
gotTranslucentFullscreen = true;
}
}
final int windowingMode = getWindowingMode();
final boolean isAssistantType = isActivityTypeAssistant();
for (int i = parent.getChildCount() - 1; i >= 0; --i) {
final WindowContainer wc = parent.getChildAt(i);
final Task other = wc.asTask();
if (other == null) continue;
final boolean hasRunningActivities = other.topRunningActivity() != null;
if (other == this) {
// Should be visible if there is no other stack occluding it, unless it doesn't
// have any running activities, not starting one and not home stack.
shouldBeVisible = hasRunningActivities || isInTask(starting) != null
|| isActivityTypeHome();
break;
}
if (!hasRunningActivities) {
continue;
}
final int otherWindowingMode = other.getWindowingMode();
if (otherWindowingMode == WINDOWING_MODE_FULLSCREEN) {
if (other.isTranslucent(starting)) {
// Can be visible behind a translucent fullscreen stack.
gotTranslucentFullscreen = true;
continue;
}
return STACK_VISIBILITY_INVISIBLE;
} else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& !gotOpaqueSplitScreenPrimary) {
gotSplitScreenStack = true;
gotTranslucentSplitScreenPrimary = other.isTranslucent(starting);
gotOpaqueSplitScreenPrimary = !gotTranslucentSplitScreenPrimary;
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
&& gotOpaqueSplitScreenPrimary) {
// Can not be visible behind another opaque stack in split-screen-primary mode.
return STACK_VISIBILITY_INVISIBLE;
}
} else if (otherWindowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
&& !gotOpaqueSplitScreenSecondary) {
gotSplitScreenStack = true;
gotTranslucentSplitScreenSecondary = other.isTranslucent(starting);
gotOpaqueSplitScreenSecondary = !gotTranslucentSplitScreenSecondary;
if (windowingMode == WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
&& gotOpaqueSplitScreenSecondary) {
// Can not be visible behind another opaque stack in split-screen-secondary mode.
return STACK_VISIBILITY_INVISIBLE;
}
}
if (gotOpaqueSplitScreenPrimary && gotOpaqueSplitScreenSecondary) {
// Can not be visible if we are in split-screen windowing mode and both halves of
// the screen are opaque.
return STACK_VISIBILITY_INVISIBLE;
}
if (isAssistantType && gotSplitScreenStack) {
// Assistant stack can't be visible behind split-screen. In addition to this not
// making sense, it also works around an issue here we boost the z-order of the
// assistant window surfaces in window manager whenever it is visible.
return STACK_VISIBILITY_INVISIBLE;
}
}
if (!shouldBeVisible) {
return STACK_VISIBILITY_INVISIBLE;
}
// Handle cases when there can be a translucent split-screen stack on top.
switch (windowingMode) {
case WINDOWING_MODE_FULLSCREEN:
if (gotTranslucentSplitScreenPrimary || gotTranslucentSplitScreenSecondary) {
// At least one of the split-screen stacks that covers this one is translucent.
return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
}
break;
case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY:
if (gotTranslucentSplitScreenPrimary) {
// Covered by translucent primary split-screen on top.
return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
}
break;
case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY:
if (gotTranslucentSplitScreenSecondary) {
// Covered by translucent secondary split-screen on top.
return STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT;
}
break;
}
// Lastly - check if there is a translucent fullscreen stack on top.
return gotTranslucentFullscreen ? STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT
: STACK_VISIBILITY_VISIBLE;
}
Task.shouldBeVisible其实质的为Task的可见性,具体逻辑判断在getVisibility中。getVisibility的返回值表述了STACK_VISIBILITY的状态,包括以下3中状态
@IntDef(prefix = {"STACK_VISIBILITY"}, value = {
STACK_VISIBILITY_VISIBLE,//正常可见
STACK_VISIBILITY_VISIBLE_BEHIND_TRANSLUCENT,//透明或者分屏下的可见
STACK_VISIBILITY_INVISIBLE,//不可见
})
通过以上Task.shouldBeVisible的处理获取到mBehindFullscreenActivity参与到ActivityRecord的判断之中
frameworks/base/services/core/java/com/android/server/wm/ActivityRecord.java
/** @return {@code true} if this activity should be made visible. */
boolean shouldBeVisible(boolean behindFullscreenActivity, boolean ignoringKeyguard) {
// Check whether activity should be visible without Keyguard influence
visibleIgnoringKeyguard = (!behindFullscreenActivity || mLaunchTaskBehind)
&& okToShowLocked();
if (ignoringKeyguard) {
return visibleIgnoringKeyguard;
}
final ActivityStack stack = getRootTask();
if (stack == null) {
return false;
}
// Activity in a pinned stack should not be visible if the stack is in force hidden state.
// Typically due to the FLAG_FORCE_HIDDEN_FOR_PINNED_TASK set on the stack, which is a
// work around to send onStop before windowing mode change callbacks.
// See also ActivityStackSupervisor#removePinnedStackInSurfaceTransaction
// TODO: Should we ever be visible if the stack/task is invisible?
if (inPinnedWindowingMode() && stack.isForceHidden()) {
return false;
}
// Now check whether it's really visible depending on Keyguard state, and update
// {@link ActivityStack} internal states.
// Inform the method if this activity is the top activity of this stack, but exclude the
// case where this is the top activity in a pinned stack.
final boolean isTop = this == stack.getTopNonFinishingActivity();
final boolean isTopNotPinnedStack = stack.isAttached()
&& stack.getDisplayArea().isTopNotPinnedStack(stack);
final boolean visibleIgnoringDisplayStatus = stack.checkKeyguardVisibility(this,
visibleIgnoringKeyguard, isTop && isTopNotPinnedStack);
// Check if the activity is on a sleeping display, and if it can turn it ON.
// TODO(b/163993448): Do not make activity visible before display awake.
if (visibleIgnoringDisplayStatus && getDisplay().isSleeping()) {
return !mSetToSleep || canTurnScreenOn();
}
return visibleIgnoringDisplayStatus;
}
visibleIgnoringKeyguard是指"忽略Keyguard可能隐藏此Activity,Activity是否可见"。ActivityRecord.shouldBeVisible主要判断锁屏、固定屏幕之间的关系。
到此就获取到了ActivityRecord的可见性,整体总结如下