Merge "Remove token behind fullscreen activity from unknown visibility"

This commit is contained in:
Riddle Hsu
2019-10-28 10:36:52 +00:00
committed by Android (Google) Code Review
4 changed files with 123 additions and 60 deletions

View File

@@ -4439,34 +4439,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
removeStartingWindow();
}
void notifyUnknownVisibilityLaunched() {
/**
* Suppress transition until the new activity becomes ready, otherwise the keyguard can appear
* for a short amount of time before the new process with the new activity had the ability to
* set its showWhenLocked flags.
*/
void notifyUnknownVisibilityLaunchedForKeyguardTransition() {
// No display activities never add a window, so there is no point in waiting them for
// relayout.
if (!noDisplay && getDisplayContent() != null) {
getDisplayContent().mUnknownAppVisibilityController.notifyLaunched(this);
}
}
/**
* @return true if the input activity should be made visible, ignoring any effect Keyguard
* might have on the visibility
*
* TODO(b/123540470): Combine this method and {@link #shouldBeVisible(boolean)}.
*
* @see {@link ActivityStack#checkKeyguardVisibility}
*/
boolean shouldBeVisibleIgnoringKeyguard(boolean behindFullscreenActivity) {
if (!okToShowLocked()) {
return false;
if (noDisplay || !mStackSupervisor.getKeyguardController().isKeyguardLocked()) {
return;
}
return !behindFullscreenActivity || mLaunchTaskBehind;
mDisplayContent.mUnknownAppVisibilityController.notifyLaunched(this);
}
boolean shouldBeVisible(boolean behindFullscreenActivity) {
/** @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 = shouldBeVisibleIgnoringKeyguard(behindFullscreenActivity);
visibleIgnoringKeyguard = (!behindFullscreenActivity || mLaunchTaskBehind)
&& okToShowLocked();
if (ignoringKeyguard) {
return visibleIgnoringKeyguard;
}
final ActivityStack stack = getActivityStack();
if (stack == null) {
@@ -4495,9 +4491,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
return false;
}
// TODO: Use real value of behindFullscreenActivity calculated using the same logic in
// ActivityStack#ensureActivitiesVisibleLocked().
return shouldBeVisible(!stack.shouldBeVisible(null /* starting */));
final boolean behindFullscreenActivity = stack.checkBehindFullscreenActivity(
this, null /* handleBehindFullscreenActivity */);
return shouldBeVisible(behindFullscreenActivity, false /* ignoringKeyguard */);
}
void makeVisibleIfNeeded(ActivityRecord starting, boolean reportToClient) {
@@ -5538,12 +5534,26 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
void removeOrphanedStartingWindow(boolean behindFullscreenActivity) {
if (mStartingWindowState == STARTING_WINDOW_SHOWN && behindFullscreenActivity) {
/**
* If any activities below the top running one are in the INITIALIZING state and they have a
* starting window displayed then remove that starting window. It is possible that the activity
* in this state will never resumed in which case that starting window will be orphaned.
* <p>
* It should only be called if this activity is behind other fullscreen activity.
*/
void cancelInitializing() {
if (mStartingWindowState == STARTING_WINDOW_SHOWN) {
// Remove orphaned starting window.
if (DEBUG_VISIBILITY) Slog.w(TAG_VISIBILITY, "Found orphaned starting window " + this);
mStartingWindowState = STARTING_WINDOW_REMOVED;
removeStartingWindow();
}
if (isState(INITIALIZING) && !shouldBeVisible(
true /* behindFullscreenActivity */, true /* ignoringKeyguard */)) {
// Remove the unknown visibility record because an invisible activity shouldn't block
// the keyguard transition.
mDisplayContent.mUnknownAppVisibilityController.appRemovedOrHidden(this);
}
}
void postWindowRemoveStartingWindowCleanup(WindowState win) {

View File

@@ -162,6 +162,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.function.Consumer;
/**
* State and management of a single stack of activities.
@@ -2118,14 +2119,21 @@ class ActivityStack extends ConfigurationContainer {
}
aboveTop = false;
final boolean reallyVisible = r.shouldBeVisible(behindFullscreenActivity,
false /* ignoringKeyguard */);
// Check whether activity should be visible without Keyguard influence
final boolean visibleIgnoringKeyguard = r.shouldBeVisibleIgnoringKeyguard(
behindFullscreenActivity);
final boolean reallyVisible = r.shouldBeVisible(behindFullscreenActivity);
if (visibleIgnoringKeyguard) {
behindFullscreenActivity = updateBehindFullscreen(!stackShouldBeVisible,
behindFullscreenActivity, r);
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=" + stackShouldBeVisible
+ " behindFullscreen=" + behindFullscreenActivity);
behindFullscreenActivity = true;
} else {
behindFullscreenActivity = false;
}
}
if (reallyVisible) {
if (r.finishing) {
continue;
@@ -2338,18 +2346,6 @@ class ActivityStack extends ConfigurationContainer {
return false;
}
private boolean updateBehindFullscreen(boolean stackInvisible, boolean behindFullscreenActivity,
ActivityRecord r) {
if (r.occludesParent()) {
if (DEBUG_VISIBILITY) Slog.v(TAG_VISIBILITY, "Fullscreen: at " + r
+ " stackInvisible=" + stackInvisible
+ " behindFullscreenActivity=" + behindFullscreenActivity);
// At this point, nothing else needs to be shown in this task.
behindFullscreenActivity = true;
}
return behindFullscreenActivity;
}
void convertActivityToTranslucent(ActivityRecord r) {
mTranslucentActivityWaiting = r;
mUndrawnActivitiesBelowTopTranslucent.clear();
@@ -2400,14 +2396,22 @@ class ActivityStack extends ConfigurationContainer {
}
}
/** If any activities below the top running one are in the INITIALIZING state and they have a
* starting window displayed then remove that starting window. It is possible that the activity
* in this state will never resumed in which case that starting window will be orphaned. */
/** @see ActivityRecord#cancelInitializing() */
void cancelInitializingActivities() {
final ActivityRecord topActivity = topRunningActivityLocked();
boolean aboveTop = true;
// We don't want to clear starting window for activities that aren't behind fullscreen
// activities as we need to display their starting window until they are done initializing.
checkBehindFullscreenActivity(null /* toCheck */, ActivityRecord::cancelInitializing);
}
/**
* If an activity {@param toCheck} is given, this method returns {@code true} if the activity
* is occluded by any fullscreen activity. If there is no {@param toCheck} and the handling
* function {@param handleBehindFullscreenActivity} is given, this method will pass all occluded
* activities to the function.
*/
boolean checkBehindFullscreenActivity(ActivityRecord toCheck,
Consumer<ActivityRecord> handleBehindFullscreenActivity) {
boolean aboveTop = true;
boolean behindFullscreenActivity = false;
if (!shouldBeVisible(null)) {
@@ -2417,22 +2421,40 @@ class ActivityStack extends ConfigurationContainer {
behindFullscreenActivity = true;
}
final boolean handlingOccluded = toCheck == null && handleBehindFullscreenActivity != null;
if (!handlingOccluded && behindFullscreenActivity) {
return true;
}
final ActivityRecord topActivity = topRunningActivityLocked();
for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {
final TaskRecord task = mTaskHistory.get(taskNdx);
for (int activityNdx = task.getChildCount() - 1; activityNdx >= 0; --activityNdx) {
final ActivityRecord r = task.getChildAt(activityNdx);
if (aboveTop) {
if (r == topActivity) {
if (r == toCheck) {
// It is the top activity in a visible stack.
return false;
}
aboveTop = false;
}
behindFullscreenActivity |= r.occludesParent();
continue;
}
r.removeOrphanedStartingWindow(behindFullscreenActivity);
if (handlingOccluded) {
handleBehindFullscreenActivity.accept(r);
} else if (r == toCheck) {
return behindFullscreenActivity;
} else if (behindFullscreenActivity) {
// It is occluded before {@param toCheck} is found.
return true;
}
behindFullscreenActivity |= r.occludesParent();
}
}
return behindFullscreenActivity;
}
/**

View File

@@ -752,9 +752,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
andResume = false;
}
if (getKeyguardController().isKeyguardLocked()) {
r.notifyUnknownVisibilityLaunched();
}
r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
// Have the window manager re-evaluate the orientation of the screen based on the new
// activity order. Note that as a result of this, it can call back into the activity
@@ -992,12 +990,7 @@ public class ActivityStackSupervisor implements RecentTasks.Callbacks {
knownToBeDead = true;
}
// Suppress transition until the new activity becomes ready, otherwise the keyguard can
// appear for a short amount of time before the new process with the new activity had the
// ability to set its showWhenLocked flags.
if (getKeyguardController().isKeyguardLocked()) {
r.notifyUnknownVisibilityLaunched();
}
r.notifyUnknownVisibilityLaunchedForKeyguardTransition();
final boolean isTop = andResume && r.isTopRunningActivity();
mService.startProcessAsync(r, knownToBeDead, isTop, isTop ? "top-activity" : "activity");

View File

@@ -1139,13 +1139,51 @@ public class ActivityStackTests extends ActivityTestsBase {
assertThat(result).isEqualTo(taskTop);
}
@Test
public void testClearUnknownAppVisibilityBehindFullscreenActivity() {
final UnknownAppVisibilityController unknownAppVisibilityController =
mDefaultDisplay.mDisplayContent.mUnknownAppVisibilityController;
final KeyguardController keyguardController = mSupervisor.getKeyguardController();
doReturn(true).when(keyguardController).isKeyguardLocked();
// Start 2 activities that their processes have not yet started.
final ActivityRecord[] activities = new ActivityRecord[2];
mSupervisor.beginDeferResume();
for (int i = 0; i < activities.length; i++) {
final ActivityRecord r = new ActivityBuilder(mService).setTask(mTask).build();
activities[i] = r;
doReturn(null).when(mService).getProcessController(
eq(r.processName), eq(r.info.applicationInfo.uid));
r.setState(ActivityStack.ActivityState.INITIALIZING, "test");
// Ensure precondition that the activity is opaque.
assertTrue(r.occludesParent());
mSupervisor.startSpecificActivityLocked(r, false /* andResume */,
false /* checkConfig */);
}
mSupervisor.endDeferResume();
doReturn(false).when(mService).isBooting();
doReturn(true).when(mService).isBooted();
// 2 activities are started while keyguard is locked, so they are waiting to be resolved.
assertFalse(unknownAppVisibilityController.allResolved());
// Assume the top activity is going to resume and
// {@link RootActivityContainer#cancelInitializingActivities} should clear the unknown
// visibility records that are occluded.
mStack.resumeTopActivityUncheckedLocked(null /* prev */, null /* options */);
// Assume the top activity relayouted, just remove it directly.
unknownAppVisibilityController.appRemovedOrHidden(activities[1]);
// All unresolved records should be removed.
assertTrue(unknownAppVisibilityController.allResolved());
}
@Test
public void testNonTopVisibleActivityNotResume() {
final ActivityRecord nonTopVisibleActivity =
new ActivityBuilder(mService).setTask(mTask).build();
new ActivityBuilder(mService).setTask(mTask).build();
doReturn(false).when(nonTopVisibleActivity).attachedToProcess();
doReturn(true).when(nonTopVisibleActivity).shouldBeVisibleIgnoringKeyguard(anyBoolean());
doReturn(true).when(nonTopVisibleActivity).shouldBeVisible(anyBoolean(), anyBoolean());
doNothing().when(mSupervisor).startSpecificActivityLocked(any(), anyBoolean(),
anyBoolean());