Loading services/core/java/com/android/server/wm/ActivityRecord.java +36 −26 Original line number Diff line number Diff line Loading @@ -4439,34 +4439,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A removeStartingWindow(); } void notifyUnknownVisibilityLaunched() { // 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} * 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. */ boolean shouldBeVisibleIgnoringKeyguard(boolean behindFullscreenActivity) { if (!okToShowLocked()) { return false; void notifyUnknownVisibilityLaunchedForKeyguardTransition() { // No display activities never add a window, so there is no point in waiting them for // relayout. 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) { Loading Loading @@ -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) { Loading Loading @@ -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) { Loading services/core/java/com/android/server/wm/ActivityStack.java +46 −24 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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)) { Loading @@ -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; } /** Loading services/core/java/com/android/server/wm/ActivityStackSupervisor.java +2 −9 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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"); Loading services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +39 −1 Original line number Diff line number Diff line Loading @@ -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()); Loading Loading
services/core/java/com/android/server/wm/ActivityRecord.java +36 −26 Original line number Diff line number Diff line Loading @@ -4439,34 +4439,30 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A removeStartingWindow(); } void notifyUnknownVisibilityLaunched() { // 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} * 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. */ boolean shouldBeVisibleIgnoringKeyguard(boolean behindFullscreenActivity) { if (!okToShowLocked()) { return false; void notifyUnknownVisibilityLaunchedForKeyguardTransition() { // No display activities never add a window, so there is no point in waiting them for // relayout. 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) { Loading Loading @@ -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) { Loading Loading @@ -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) { Loading
services/core/java/com/android/server/wm/ActivityStack.java +46 −24 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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)) { Loading @@ -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; } /** Loading
services/core/java/com/android/server/wm/ActivityStackSupervisor.java +2 −9 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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"); Loading
services/tests/wmtests/src/com/android/server/wm/ActivityStackTests.java +39 −1 Original line number Diff line number Diff line Loading @@ -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()); Loading