1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.am;
18
19import static android.app.ActivityManager.START_DELIVERED_TO_TOP;
20import static android.app.ActivityManager.START_TASK_TO_FRONT;
21import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
22import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
23import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY;
24import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
25import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
26import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
27import static android.content.pm.ActivityInfo.FLAG_SHOW_WHEN_LOCKED;
28
29import static com.android.server.am.ActivityStack.REMOVE_TASK_MODE_DESTROYING;
30import static com.android.server.am.ActivityStackSupervisor
31        .MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE;
32
33import static org.junit.Assert.assertEquals;
34import static org.junit.Assert.assertFalse;
35import static org.junit.Assert.assertNull;
36import static org.junit.Assert.assertTrue;
37import static org.mockito.ArgumentMatchers.any;
38import static org.mockito.Matchers.anyInt;
39import static org.mockito.Mockito.doAnswer;
40import static org.mockito.Mockito.doReturn;
41import static org.mockito.Mockito.mock;
42import static org.mockito.Mockito.reset;
43import static org.mockito.Mockito.times;
44import static org.mockito.Mockito.verify;
45
46import android.app.ActivityOptions;
47import android.app.WaitResult;
48import android.graphics.Rect;
49import android.platform.test.annotations.Presubmit;
50import android.support.test.filters.MediumTest;
51import android.support.test.runner.AndroidJUnit4;
52import android.util.SparseIntArray;
53
54import org.junit.Before;
55import org.junit.Test;
56import org.junit.runner.RunWith;
57import org.mockito.invocation.InvocationOnMock;
58
59import java.util.ArrayList;
60
61/**
62 * Tests for the {@link ActivityStackSupervisor} class.
63 *
64 * Build/Install/Run:
65 *  atest FrameworksServicesTests:com.android.server.am.ActivityStackSupervisorTests
66 */
67@MediumTest
68@Presubmit
69@RunWith(AndroidJUnit4.class)
70public class ActivityStackSupervisorTests extends ActivityTestsBase {
71    private ActivityManagerService mService;
72    private ActivityStackSupervisor mSupervisor;
73    private ActivityStack mFullscreenStack;
74
75    @Before
76    @Override
77    public void setUp() throws Exception {
78        super.setUp();
79
80        mService = createActivityManagerService();
81        mSupervisor = mService.mStackSupervisor;
82        mFullscreenStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
83                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
84    }
85
86    /**
87     * This test ensures that we do not try to restore a task based off an invalid task id. The
88     * stack supervisor is a test version so there will be no tasks present. We should expect
89     * {@code null} to be returned in this case.
90     */
91    @Test
92    public void testRestoringInvalidTask() throws Exception {
93        TaskRecord task = mSupervisor.anyTaskForIdLocked(0 /*taskId*/,
94                MATCH_TASK_IN_STACKS_OR_RECENT_TASKS_AND_RESTORE, null, false /* onTop */);
95        assertNull(task);
96    }
97
98    /**
99     * This test ensures that an existing task in the pinned stack is moved to the fullscreen
100     * activity stack when a new task is added.
101     */
102    @Test
103    public void testReplacingTaskInPinnedStack() throws Exception {
104        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
105                .setStack(mFullscreenStack).build();
106        final TaskRecord firstTask = firstActivity.getTask();
107
108        final ActivityRecord secondActivity = new ActivityBuilder(mService).setCreateTask(true)
109                .setStack(mFullscreenStack).build();
110        final TaskRecord secondTask = secondActivity.getTask();
111
112        mSupervisor.setFocusStackUnchecked("testReplacingTaskInPinnedStack", mFullscreenStack);
113
114        // Ensure full screen stack has both tasks.
115        ensureStackPlacement(mFullscreenStack, firstTask, secondTask);
116
117        // Move first activity to pinned stack.
118        final Rect sourceBounds = new Rect();
119        mSupervisor.moveActivityToPinnedStackLocked(firstActivity, sourceBounds,
120                0f /*aspectRatio*/, "initialMove");
121
122        final ActivityDisplay display = mFullscreenStack.getDisplay();
123        ActivityStack pinnedStack = display.getPinnedStack();
124        // Ensure a task has moved over.
125        ensureStackPlacement(pinnedStack, firstTask);
126        ensureStackPlacement(mFullscreenStack, secondTask);
127
128        // Move second activity to pinned stack.
129        mSupervisor.moveActivityToPinnedStackLocked(secondActivity, sourceBounds,
130                0f /*aspectRatio*/, "secondMove");
131
132        // Need to get stacks again as a new instance might have been created.
133        pinnedStack = display.getPinnedStack();
134        mFullscreenStack = display.getStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
135        // Ensure stacks have swapped tasks.
136        ensureStackPlacement(pinnedStack, secondTask);
137        ensureStackPlacement(mFullscreenStack, firstTask);
138    }
139
140    private static void ensureStackPlacement(ActivityStack stack, TaskRecord... tasks) {
141        final ArrayList<TaskRecord> stackTasks = stack.getAllTasks();
142        assertEquals(stackTasks.size(), tasks != null ? tasks.length : 0);
143
144        if (tasks == null) {
145            return;
146        }
147
148        for (TaskRecord task : tasks) {
149            assertTrue(stackTasks.contains(task));
150        }
151    }
152
153    /**
154     * Ensures that an activity is removed from the stopping activities list once it is resumed.
155     */
156    @Test
157    public void testStoppingActivityRemovedWhenResumed() throws Exception {
158        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
159                .setStack(mFullscreenStack).build();
160        mSupervisor.mStoppingActivities.add(firstActivity);
161
162        firstActivity.completeResumeLocked();
163
164        assertFalse(mSupervisor.mStoppingActivities.contains(firstActivity));
165    }
166
167    /**
168     * Ensures that waiting results are notified of launches.
169     */
170    @Test
171    public void testReportWaitingActivityLaunchedIfNeeded() throws Exception {
172        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
173                .setStack(mFullscreenStack).build();
174
175        // #notifyAll will be called on the ActivityManagerService. we must hold the object lock
176        // when this happens.
177        synchronized (mSupervisor.mService) {
178            final WaitResult taskToFrontWait = new WaitResult();
179            mSupervisor.mWaitingActivityLaunched.add(taskToFrontWait);
180            mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity, START_TASK_TO_FRONT);
181
182            assertTrue(mSupervisor.mWaitingActivityLaunched.isEmpty());
183            assertEquals(taskToFrontWait.result, START_TASK_TO_FRONT);
184            assertEquals(taskToFrontWait.who, null);
185
186            final WaitResult deliverToTopWait = new WaitResult();
187            mSupervisor.mWaitingActivityLaunched.add(deliverToTopWait);
188            mSupervisor.reportWaitingActivityLaunchedIfNeeded(firstActivity,
189                    START_DELIVERED_TO_TOP);
190
191            assertTrue(mSupervisor.mWaitingActivityLaunched.isEmpty());
192            assertEquals(deliverToTopWait.result, START_DELIVERED_TO_TOP);
193            assertEquals(deliverToTopWait.who, firstActivity.realActivity);
194        }
195    }
196
197    @Test
198    public void testApplySleepTokensLocked() throws Exception {
199        final ActivityDisplay display = mSupervisor.getDefaultDisplay();
200        final KeyguardController keyguard = mSupervisor.getKeyguardController();
201        final ActivityStack stack = mock(ActivityStack.class);
202        display.addChild(stack, 0 /* position */);
203
204        // Make sure we wake and resume in the case the display is turning on and the keyguard is
205        // not showing.
206        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
207                false /* displayShouldSleep */, true /* isFocusedStack */,
208                false /* keyguardShowing */, true /* expectWakeFromSleep */,
209                true /* expectResumeTopActivity */);
210
211        // Make sure we wake and don't resume when the display is turning on and the keyguard is
212        // showing.
213        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
214                false /* displayShouldSleep */, true /* isFocusedStack */,
215                true /* keyguardShowing */, true /* expectWakeFromSleep */,
216                false /* expectResumeTopActivity */);
217
218        // Make sure we wake and don't resume when the display is turning on and the keyguard is
219        // not showing as unfocused.
220        verifySleepTokenBehavior(display, keyguard, stack, true /*displaySleeping*/,
221                false /* displayShouldSleep */, false /* isFocusedStack */,
222                false /* keyguardShowing */, true /* expectWakeFromSleep */,
223                false /* expectResumeTopActivity */);
224
225        // Should not do anything if the display state hasn't changed.
226        verifySleepTokenBehavior(display, keyguard, stack, false /*displaySleeping*/,
227                false /* displayShouldSleep */, true /* isFocusedStack */,
228                false /* keyguardShowing */, false /* expectWakeFromSleep */,
229                false /* expectResumeTopActivity */);
230    }
231
232    private void verifySleepTokenBehavior(ActivityDisplay display, KeyguardController keyguard,
233            ActivityStack stack, boolean displaySleeping, boolean displayShouldSleep,
234            boolean isFocusedStack, boolean keyguardShowing, boolean expectWakeFromSleep,
235            boolean expectResumeTopActivity) {
236        reset(stack);
237
238        doReturn(displayShouldSleep).when(display).shouldSleep();
239        doReturn(displaySleeping).when(display).isSleeping();
240        doReturn(keyguardShowing).when(keyguard).isKeyguardOrAodShowing(anyInt());
241
242        mSupervisor.mFocusedStack = isFocusedStack ? stack : null;
243        mSupervisor.applySleepTokensLocked(true);
244        verify(stack, times(expectWakeFromSleep ? 1 : 0)).awakeFromSleepingLocked();
245        verify(stack, times(expectResumeTopActivity ? 1 : 0)).resumeTopActivityUncheckedLocked(
246                null /* target */, null /* targetOptions */);
247    }
248
249    @Test
250    public void testTopRunningActivityLockedWithNonExistentDisplay() throws Exception {
251        // Create display that ActivityManagerService does not know about
252        final int unknownDisplayId = 100;
253
254        doAnswer((InvocationOnMock invocationOnMock) -> {
255            final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
256            displayIds.put(0, unknownDisplayId);
257            return null;
258        }).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
259
260        mSupervisor.mFocusedStack = mock(ActivityStack.class);
261
262        // Supervisor should skip over the non-existent display.
263        assertEquals(null, mSupervisor.topRunningActivityLocked());
264    }
265
266    /**
267     * Verifies that removal of activity with task and stack is done correctly.
268     */
269    @Test
270    public void testRemovingStackOnAppCrash() throws Exception {
271        final ActivityDisplay defaultDisplay = mService.mStackSupervisor.getDefaultDisplay();
272        final int originalStackCount = defaultDisplay.getChildCount();
273        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
274                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, false /* onTop */);
275        final ActivityRecord firstActivity = new ActivityBuilder(mService).setCreateTask(true)
276                .setStack(stack).build();
277
278        assertEquals(originalStackCount + 1, defaultDisplay.getChildCount());
279
280        // Let's pretend that the app has crashed.
281        firstActivity.app.thread = null;
282        mService.mStackSupervisor.finishTopCrashedActivitiesLocked(firstActivity.app, "test");
283
284        // Verify that the stack was removed.
285        assertEquals(originalStackCount, defaultDisplay.getChildCount());
286    }
287
288    @Test
289    public void testFocusability() throws Exception {
290        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
291                WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD, true /* onTop */);
292        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
293                .setStack(stack).build();
294
295        // Under split screen primary we should be focusable when not minimized
296        mService.mStackSupervisor.setDockedStackMinimized(false);
297        assertTrue(stack.isFocusable());
298        assertTrue(activity.isFocusable());
299
300        // Under split screen primary we should not be focusable when minimized
301        mService.mStackSupervisor.setDockedStackMinimized(true);
302        assertFalse(stack.isFocusable());
303        assertFalse(activity.isFocusable());
304
305        final ActivityStack pinnedStack = mService.mStackSupervisor.getDefaultDisplay().createStack(
306                WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD, true /* onTop */);
307        final ActivityRecord pinnedActivity = new ActivityBuilder(mService).setCreateTask(true)
308                .setStack(pinnedStack).build();
309
310        // We should not be focusable when in pinned mode
311        assertFalse(pinnedStack.isFocusable());
312        assertFalse(pinnedActivity.isFocusable());
313
314        // Add flag forcing focusability.
315        pinnedActivity.info.flags |= FLAG_ALWAYS_FOCUSABLE;
316
317        // We should not be focusable when in pinned mode
318        assertTrue(pinnedStack.isFocusable());
319        assertTrue(pinnedActivity.isFocusable());
320
321        // Without the overridding activity, stack should not be focusable.
322        pinnedStack.removeTask(pinnedActivity.getTask(), "testFocusability",
323                REMOVE_TASK_MODE_DESTROYING);
324        assertFalse(pinnedStack.isFocusable());
325    }
326
327    /**
328     * Verifies the correct activity is returned when querying the top running activity.
329     */
330    @Test
331    public void testTopRunningActivity() throws Exception {
332        // Create stack to hold focus
333        final ActivityStack emptyStack = mService.mStackSupervisor.getDefaultDisplay()
334                .createStack(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
335
336        final KeyguardController keyguard = mSupervisor.getKeyguardController();
337        final ActivityStack stack = mService.mStackSupervisor.getDefaultDisplay().createStack(
338                WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD, true /* onTop */);
339        final ActivityRecord activity = new ActivityBuilder(mService).setCreateTask(true)
340                .setStack(stack).build();
341
342        mSupervisor.mFocusedStack = emptyStack;
343
344        doAnswer((InvocationOnMock invocationOnMock) -> {
345            final SparseIntArray displayIds = invocationOnMock.<SparseIntArray>getArgument(0);
346            displayIds.put(0, mSupervisor.getDefaultDisplay().mDisplayId);
347            return null;
348        }).when(mSupervisor.mWindowManager).getDisplaysInFocusOrder(any());
349
350        // Make sure the top running activity is not affected when keyguard is not locked
351        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
352        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked(
353                true /* considerKeyguardState */));
354
355        // Check to make sure activity not reported when it cannot show on lock and lock is on.
356        doReturn(true).when(keyguard).isKeyguardLocked();
357        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
358        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
359                true /* considerKeyguardState */));
360
361        // Change focus to stack with activity.
362        mSupervisor.mFocusedStack = stack;
363        assertEquals(activity, mService.mStackSupervisor.topRunningActivityLocked());
364        assertEquals(null, mService.mStackSupervisor.topRunningActivityLocked(
365                true /* considerKeyguardState */));
366
367        // Add activity that should be shown on the keyguard.
368        final ActivityRecord showWhenLockedActivity = new ActivityBuilder(mService)
369                .setCreateTask(true)
370                .setStack(stack)
371                .setActivityFlags(FLAG_SHOW_WHEN_LOCKED)
372                .build();
373
374        // Ensure the show when locked activity is returned.
375        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
376        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
377                true /* considerKeyguardState */));
378
379        // Change focus back to empty stack
380        mSupervisor.mFocusedStack = emptyStack;
381        // Ensure the show when locked activity is returned when not the focused stack
382        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked());
383        assertEquals(showWhenLockedActivity, mService.mStackSupervisor.topRunningActivityLocked(
384                true /* considerKeyguardState */));
385    }
386
387    /**
388     * Verify that split-screen primary stack will be chosen if activity is launched that targets
389     * split-screen secondary, but a matching existing instance is found on top of split-screen
390     * primary stack.
391     */
392    @Test
393    public void testSplitScreenPrimaryChosenWhenTopActivityLaunchedToSecondary() throws Exception {
394        // Create primary split-screen stack with a task and an activity.
395        final ActivityStack primaryStack = mService.mStackSupervisor.getDefaultDisplay()
396                .createStack(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, ACTIVITY_TYPE_STANDARD,
397                        true /* onTop */);
398        final TaskRecord task = new TaskBuilder(mSupervisor).setStack(primaryStack).build();
399        final ActivityRecord r = new ActivityBuilder(mService).setTask(task).build();
400
401        // Find a launch stack for the top activity in split-screen primary, while requesting
402        // split-screen secondary.
403        final ActivityOptions options = ActivityOptions.makeBasic();
404        options.setLaunchWindowingMode(WINDOWING_MODE_FULLSCREEN_OR_SPLIT_SCREEN_SECONDARY);
405        final ActivityStack result = mSupervisor.getLaunchStack(r, options, task, true /* onTop */);
406
407        // Assert that the primary stack is returned.
408        assertEquals(primaryStack, result);
409    }
410}
411