Merge "Remove token behind fullscreen activity from unknown visibility"
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
Reference in New Issue
Block a user