From c961e0a52b6884914c1edb450e47edd1e2bd8ef1 Mon Sep 17 00:00:00 2001 From: Bryce Lee Date: Fri, 13 Apr 2018 17:58:02 -0700 Subject: [PATCH] Consider lock state when determining next activity to become visible. When an activity shown on top of the lock screen finishes, the next activity to become visible is considered. This activity is determined by the supervisor's top running activity. When the focused stack does not have a top running activity, the top most non-focused stack's top running activity is considered. In the case the device is locked, this value may be an activity that does not show on lock screen. As a result, the finishing activity will wait indefinitely for an activity that will never appear. This changelist introduces the ability to ask for the top running activity taking the current locked state into consideration. When locked, only activities that can show on top of the lock screen will be returned if we consider non-focused stacks. Change-Id: I235e345c0b1d255b44d147bc71069c164389db4f Fixes: 76424176 Test: atest FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests#testNonFocusedTopRunningActivity --- .../com/android/server/am/ActivityStack.java | 6 ++- .../server/am/ActivityStackSupervisor.java | 45 ++++++++++++++--- .../am/ActivityStackSupervisorTests.java | 49 +++++++++++++++++++ .../android/server/am/ActivityTestsBase.java | 8 +++ 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java index e20356ff12dbf..511391a33c68f 100644 --- a/services/core/java/com/android/server/am/ActivityStack.java +++ b/services/core/java/com/android/server/am/ActivityStack.java @@ -3797,7 +3797,11 @@ class ActivityStack extends ConfigurationContai // and the resumed activity is not yet visible, then hold off on // finishing until the resumed one becomes visible. - final ActivityRecord next = mStackSupervisor.topRunningActivityLocked(); + // The activity that we are finishing may be over the lock screen. In this case, we do not + // want to consider activities that cannot be shown on the lock screen as running and should + // proceed with finishing the activity if there is no valid next top running activity. + final ActivityRecord next = mStackSupervisor.topRunningActivityLocked( + true /* considerKeyguardState */); if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible) && next != null && !next.nowVisible) { diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java index 6a3587c69dfba..d7e9cda9fb4d5 100644 --- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java +++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java @@ -1195,6 +1195,18 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D } ActivityRecord topRunningActivityLocked() { + return topRunningActivityLocked(false /* considerKeyguardState */); + } + + /** + * Returns the top running activity in the focused stack. In the case the focused stack has no + * such activity, the next focusable stack on top of a display is returned. + * @param considerKeyguardState Indicates whether the locked state should be considered. if + * {@code true} and the keyguard is locked, only activities that + * can be shown on top of the keyguard will be considered. + * @return The top running activity. {@code null} if none is available. + */ + ActivityRecord topRunningActivityLocked(boolean considerKeyguardState) { final ActivityStack focusedStack = mFocusedStack; ActivityRecord r = focusedStack.topRunningActivityLocked(); if (r != null) { @@ -1213,16 +1225,33 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D if (display == null) { continue; } - for (int j = display.getChildCount() - 1; j >= 0; --j) { - final ActivityStack stack = display.getChildAt(j); - if (stack != focusedStack && stack.isTopStackOnDisplay() && stack.isFocusable()) { - r = stack.topRunningActivityLocked(); - if (r != null) { - return r; - } - } + + // TODO: We probably want to consider the top fullscreen stack as we could have a pinned + // stack on top. + final ActivityStack topStack = display.getTopStack(); + + // Only consider focusable top stacks other than the current focused one. + if (topStack == null || !topStack.isFocusable() || topStack == focusedStack) { + continue; + } + + final ActivityRecord topActivity = topStack.topRunningActivityLocked(); + + // Skip if no top activity. + if (topActivity == null) { + continue; + } + + final boolean keyguardLocked = getKeyguardController().isKeyguardLocked(); + + // This activity can be considered the top running activity if we are not + // considering the locked state, the keyguard isn't locked, or we can show when + // locked. + if (!considerKeyguardState || !keyguardLocked || topActivity.canShowWhenLocked()) { + return topActivity; } } + return null; } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java index 08b8af289bd82..9daea1afc505a 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityStackSupervisorTests.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY; import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE; +import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED; import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -329,4 +330,52 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase { REMOVE_TASK_MODE_DESTROYING); assertFalse(pinnedStack.isFocusable()); } + + /** + * Verifies the correct activity is returned when querying the top running activity with an + * empty focused stack. + */ + @Test + public void testNonFocusedTopRunningActivity() throws Exception { + // Create stack to hold focus + final ActivityStack focusedStack = mService.mStackSupervisor.getDefaultDisplay() + .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + + final KeyguardController keyguard = mSupervisor.getKeyguardController(); + final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack( + WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */); + final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true) + .setStack(stack).build(); + + mSupervisor.mFocusedStack = focusedStack; + + doAnswer((InvocationOnMock invocationOnMock) -> { + final SparseIntArray displayIds = invocationOnMock.getArgument(0); + displayIds.put(0, mSupervisor.getDefaultDisplay().mDisplayId); + return null; + }).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any()); + + // Make sure the top running activity is not affected when keyguard is not locked + assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked()); + assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked( + true /* considerKeyguardState */)); + + // Check to make sure activity not reported when it cannot show on lock and lock is on. + doReturn(true).when(keyguard).isKeyguardLocked(); + assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked()); + assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked( + true /* considerKeyguardState */)); + + // Add activity that should be shown on the keyguard. + final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService) + .setCreateTask(true) + .setStack(stack) + .setActivityFlags(FLAG_SHOW_WHEN_LOCKED) + .build(); + + // Ensure the show when locked activity is returned. + assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked()); + assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked( + true /* considerKeyguardState */)); + } } diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java index f5e61a1db341b..1cd111fce0ec0 100644 --- a/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java +++ b/services/tests/servicestests/src/com/android/server/am/ActivityTestsBase.java @@ -132,6 +132,7 @@ public class ActivityTestsBase { private int mUid; private boolean mCreateTask; private ActivityStack mStack; + private int mActivityFlags; ActivityBuilder(ActivityManagerService service) { mService = service; @@ -152,6 +153,11 @@ public class ActivityTestsBase { return this; } + ActivityBuilder setActivityFlags(int flags) { + mActivityFlags = flags; + return this; + } + ActivityBuilder setStack(ActivityStack stack) { mStack = stack; return this; @@ -186,6 +192,8 @@ public class ActivityTestsBase { aInfo.applicationInfo = new ApplicationInfo(); aInfo.applicationInfo.packageName = mComponent.getPackageName(); aInfo.applicationInfo.uid = mUid; + aInfo.flags |= mActivityFlags; + final ActivityRecord activity = new ActivityRecord(mService, null /* caller */, 0 /* launchedFromPid */, 0, null, intent, null, aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,