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 android.app.ActivityOptions;
20import android.content.pm.ActivityInfo.WindowLayout;
21import android.platform.test.annotations.Presubmit;
22import android.support.test.filters.MediumTest;
23import android.support.test.runner.AndroidJUnit4;
24
25import com.android.server.am.LaunchParamsController.LaunchParams;
26import org.junit.runner.RunWith;
27import org.junit.Before;
28import org.junit.Test;
29
30import com.android.server.am.LaunchParamsController.LaunchParamsModifier;
31
32import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
33import static org.mockito.Mockito.any;
34import static org.mockito.Mockito.anyInt;
35import static org.mockito.Mockito.doNothing;
36import static org.mockito.Mockito.eq;
37import static org.mockito.Mockito.mock;
38import static org.mockito.Mockito.never;
39import static org.mockito.Mockito.spy;
40import static org.mockito.Mockito.times;
41import static org.mockito.Mockito.verify;
42
43import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_DONE;
44import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_CONTINUE;
45import static com.android.server.am.LaunchParamsController.LaunchParamsModifier.RESULT_SKIP;
46
47import static org.junit.Assert.assertEquals;
48import static org.junit.Assert.assertNotEquals;
49
50/**
51 * Tests for exercising {@link LaunchParamsController}.
52 *
53 * Build/Install/Run:
54 *  atest FrameworksServicesTests:LaunchParamsControllerTests
55 */
56@MediumTest
57@Presubmit
58@RunWith(AndroidJUnit4.class)
59public class LaunchParamsControllerTests extends ActivityTestsBase {
60    private ActivityManagerService mService;
61    private LaunchParamsController mController;
62
63    @Before
64    @Override
65    public void setUp() throws Exception {
66        super.setUp();
67        mService = createActivityManagerService();
68        mController = new LaunchParamsController(mService);
69    }
70
71    /**
72     * Makes sure positioners get values passed to controller.
73     */
74    @Test
75    public void testArgumentPropagation() {
76        final LaunchParamsModifier
77                positioner = mock(LaunchParamsModifier.class);
78        mController.registerModifier(positioner);
79
80        final ActivityRecord record = new ActivityBuilder(mService).build();
81        final ActivityRecord source = new ActivityBuilder(mService).build();
82        final WindowLayout layout = new WindowLayout(0, 0, 0, 0, 0, 0, 0);
83        final ActivityOptions options = mock(ActivityOptions.class);
84
85        mController.calculate(record.getTask(), layout, record, source, options,
86                new LaunchParams());
87        verify(positioner, times(1)).onCalculate(eq(record.getTask()), eq(layout), eq(record),
88                eq(source), eq(options), any(), any());
89    }
90
91    /**
92     * Ensures positioners further down the chain are not called when RESULT_DONE is returned.
93     */
94    @Test
95    public void testEarlyExit() {
96        final LaunchParamsModifier
97                ignoredPositioner = mock(LaunchParamsModifier.class);
98        final LaunchParamsModifier earlyExitPositioner =
99                (task, layout, activity, source, options, currentParams, outParams) -> RESULT_DONE;
100
101        mController.registerModifier(ignoredPositioner);
102        mController.registerModifier(earlyExitPositioner);
103
104        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
105                null /*source*/, null /*options*/, new LaunchParams());
106        verify(ignoredPositioner, never()).onCalculate(any(), any(), any(), any(), any(),
107                any(), any());
108    }
109
110    /**
111     * Ensures that positioners are called in the correct order.
112     */
113    @Test
114    public void testRegistration() {
115        LaunchParamsModifier earlyExitPositioner =
116                new InstrumentedPositioner(RESULT_DONE, new LaunchParams());
117
118        final LaunchParamsModifier firstPositioner = spy(earlyExitPositioner);
119
120        mController.registerModifier(firstPositioner);
121
122        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
123                null /*source*/, null /*options*/, new LaunchParams());
124        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
125                any());
126
127        final LaunchParamsModifier secondPositioner = spy(earlyExitPositioner);
128
129        mController.registerModifier(secondPositioner);
130
131        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/,
132                null /*source*/, null /*options*/, new LaunchParams());
133        verify(firstPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
134                any());
135        verify(secondPositioner, times(1)).onCalculate(any(), any(), any(), any(), any(), any(),
136                any());
137    }
138
139    /**
140     * Makes sure positioners further down the registration chain are called.
141     */
142    @Test
143    public void testPassThrough() {
144        final LaunchParamsModifier
145                positioner1 = mock(LaunchParamsModifier.class);
146        final LaunchParams params = new LaunchParams();
147        params.mWindowingMode = WINDOWING_MODE_FREEFORM;
148        params.mBounds.set(0, 0, 30, 20);
149        params.mPreferredDisplayId = 3;
150
151        final InstrumentedPositioner positioner2 = new InstrumentedPositioner(RESULT_CONTINUE,
152                params);
153
154        mController.registerModifier(positioner1);
155        mController.registerModifier(positioner2);
156
157        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
158                null /*options*/, new LaunchParams());
159
160        verify(positioner1, times(1)).onCalculate(any(), any(), any(), any(), any(),
161                eq(positioner2.getLaunchParams()), any());
162    }
163
164    /**
165     * Ensures skipped results are not propagated.
166     */
167    @Test
168    public void testSkip() {
169        final LaunchParams params1 = new LaunchParams();
170        params1.mBounds.set(0, 0, 10, 10);
171        final InstrumentedPositioner positioner1 = new InstrumentedPositioner(RESULT_SKIP, params1);
172
173        final LaunchParams params2 = new LaunchParams();
174        params2.mBounds.set(0, 0, 20, 30);
175        final InstrumentedPositioner positioner2 =
176                new InstrumentedPositioner(RESULT_CONTINUE, params2);
177
178        mController.registerModifier(positioner1);
179        mController.registerModifier(positioner2);
180
181        final LaunchParams
182                result = new LaunchParams();
183
184        mController.calculate(null /*task*/, null /*layout*/, null /*activity*/, null /*source*/,
185                null /*options*/, result);
186
187        assertEquals(result, positioner2.getLaunchParams());
188    }
189
190    /**
191     * Ensures that {@link LaunchParamsModifier} requests specifying display id during
192     * layout are honored.
193     */
194    @Test
195    public void testLayoutTaskPreferredDisplayChange() {
196        final LaunchParams params = new LaunchParams();
197        params.mPreferredDisplayId = 2;
198        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
199        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).build();
200
201        mController.registerModifier(positioner);
202
203        doNothing().when(mService).moveStackToDisplay(anyInt(), anyInt());
204        mController.layoutTask(task, null /* windowLayout */);
205        verify(mService, times(1)).moveStackToDisplay(eq(task.getStackId()),
206                eq(params.mPreferredDisplayId));
207    }
208
209    /**
210     * Ensures that {@link LaunchParamsModifier} requests specifying windowingMode during
211     * layout are honored.
212     */
213    @Test
214    public void testLayoutTaskWindowingModeChange() {
215        final LaunchParams params = new LaunchParams();
216        final int windowingMode = WINDOWING_MODE_FREEFORM;
217        params.mWindowingMode = windowingMode;
218        final InstrumentedPositioner positioner = new InstrumentedPositioner(RESULT_DONE, params);
219        final TaskRecord task = new TaskBuilder(mService.mStackSupervisor).build();
220
221        mController.registerModifier(positioner);
222
223        final int beforeWindowMode = task.getStack().getWindowingMode();
224        assertNotEquals(beforeWindowMode, windowingMode);
225
226        mController.layoutTask(task, null /* windowLayout */);
227
228        final int afterWindowMode = task.getStack().getWindowingMode();
229        assertEquals(afterWindowMode, windowingMode);
230    }
231
232    public static class InstrumentedPositioner implements
233            LaunchParamsModifier {
234
235        final private int mReturnVal;
236        final private LaunchParams mParams;
237
238        InstrumentedPositioner(int returnVal, LaunchParams params) {
239            mReturnVal = returnVal;
240            mParams = params;
241        }
242
243        @Override
244        public int onCalculate(TaskRecord task, WindowLayout layout, ActivityRecord activity,
245                   ActivityRecord source, ActivityOptions options,
246                   LaunchParams currentParams, LaunchParams outParams) {
247            outParams.set(mParams);
248            return mReturnVal;
249        }
250
251        LaunchParams getLaunchParams() {
252            return mParams;
253        }
254    }
255}
256