Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c961e0a5 authored by Bryce Lee's avatar Bryce Lee
Browse files

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
parent 069c5b58
Loading
Loading
Loading
Loading
+5 −1
Original line number Original line Diff line number Diff line
@@ -3797,7 +3797,11 @@ class ActivityStack<T extends StackWindowController> extends ConfigurationContai
        // and the resumed activity is not yet visible, then hold off on
        // and the resumed activity is not yet visible, then hold off on
        // finishing until the resumed one becomes visible.
        // 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)
        if (mode == FINISH_AFTER_VISIBLE && (r.visible || r.nowVisible)
                && next != null && !next.nowVisible) {
                && next != null && !next.nowVisible) {
+37 −8
Original line number Original line Diff line number Diff line
@@ -1195,6 +1195,18 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
    }
    }


    ActivityRecord topRunningActivityLocked() {
    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;
        final ActivityStack focusedStack = mFocusedStack;
        ActivityRecord r = focusedStack.topRunningActivityLocked();
        ActivityRecord r = focusedStack.topRunningActivityLocked();
        if (r != null) {
        if (r != null) {
@@ -1213,16 +1225,33 @@ public class ActivityStackSupervisor extends ConfigurationContainer implements D
            if (display == null) {
            if (display == null) {
                continue;
                continue;
            }
            }
            for (int j = display.getChildCount() - 1; j >= 0; --j) {

                final ActivityStack stack = display.getChildAt(j);
            // TODO: We probably want to consider the top fullscreen stack as we could have a pinned
                if (stack != focusedStack && stack.isTopStackOnDisplay() && stack.isFocusable()) {
            // stack on top.
                    r = stack.topRunningActivityLocked();
            final ActivityStack topStack = display.getTopStack();
                    if (r != null) {

                        return r;
            // 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;
        return null;
    }
    }


+49 −0
Original line number Original line Diff line number Diff line
@@ -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.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
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 com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNull;
@@ -329,4 +330,52 @@ public class ActivityStackSupervisorTests extends ActivityTestsBase {
                REMOVE_TASK_MODE_DESTROYING);
                REMOVE_TASK_MODE_DESTROYING);
        assertFalse(pinnedStack.isFocusable());
        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.<SparseIntArray>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 */));
    }
}
}
+8 −0
Original line number Original line Diff line number Diff line
@@ -132,6 +132,7 @@ public class ActivityTestsBase {
        private int mUid;
        private int mUid;
        private boolean mCreateTask;
        private boolean mCreateTask;
        private ActivityStack mStack;
        private ActivityStack mStack;
        private int mActivityFlags;


        ActivityBuilder(ActivityManagerService service) {
        ActivityBuilder(ActivityManagerService service) {
            mService = service;
            mService = service;
@@ -152,6 +153,11 @@ public class ActivityTestsBase {
            return this;
            return this;
        }
        }


        ActivityBuilder setActivityFlags(int flags) {
            mActivityFlags = flags;
            return this;
        }

        ActivityBuilder setStack(ActivityStack stack) {
        ActivityBuilder setStack(ActivityStack stack) {
            mStack = stack;
            mStack = stack;
            return this;
            return this;
@@ -186,6 +192,8 @@ public class ActivityTestsBase {
            aInfo.applicationInfo = new ApplicationInfo();
            aInfo.applicationInfo = new ApplicationInfo();
            aInfo.applicationInfo.packageName = mComponent.getPackageName();
            aInfo.applicationInfo.packageName = mComponent.getPackageName();
            aInfo.applicationInfo.uid = mUid;
            aInfo.applicationInfo.uid = mUid;
            aInfo.flags |= mActivityFlags;

            final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
            final ActivityRecord activity = new ActivityRecord(mService, null /* caller */,
                    0 /* launchedFromPid */, 0, null, intent, null,
                    0 /* launchedFromPid */, 0, null, intent, null,
                    aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,
                    aInfo /*aInfo*/, new Configuration(), null /* resultTo */, null /* resultWho */,