1 /*
2  * Copyright (C) 2019 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 
17 package com.android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
21 import static android.content.res.Configuration.GRAMMATICAL_GENDER_NOT_SPECIFIED;
22 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
23 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
24 
25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
26 import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
27 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
28 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
29 import static com.android.server.wm.ActivityRecord.State.PAUSED;
30 import static com.android.server.wm.ActivityRecord.State.PAUSING;
31 import static com.android.server.wm.ActivityRecord.State.RESUMED;
32 import static com.android.server.wm.ActivityRecord.State.STARTED;
33 import static com.android.server.wm.ActivityRecord.State.STOPPED;
34 import static com.android.server.wm.ActivityRecord.State.STOPPING;
35 
36 import static org.junit.Assert.assertEquals;
37 import static org.junit.Assert.assertFalse;
38 import static org.junit.Assert.assertNull;
39 import static org.junit.Assert.assertTrue;
40 import static org.mockito.ArgumentMatchers.any;
41 import static org.mockito.ArgumentMatchers.anyBoolean;
42 import static org.mockito.ArgumentMatchers.anyInt;
43 import static org.mockito.ArgumentMatchers.eq;
44 import static org.mockito.Mockito.clearInvocations;
45 import static org.mockito.Mockito.doReturn;
46 import static org.mockito.Mockito.mock;
47 import static org.mockito.Mockito.times;
48 import static org.mockito.Mockito.when;
49 
50 import android.Manifest;
51 import android.app.ActivityManager;
52 import android.app.ClientTransactionHandler;
53 import android.app.IApplicationThread;
54 import android.app.servertransaction.ConfigurationChangeItem;
55 import android.content.ComponentName;
56 import android.content.pm.ApplicationInfo;
57 import android.content.pm.ServiceInfo;
58 import android.content.res.Configuration;
59 import android.graphics.Rect;
60 import android.os.LocaleList;
61 import android.os.RemoteException;
62 import android.platform.test.annotations.Presubmit;
63 
64 import org.junit.Before;
65 import org.junit.Test;
66 import org.junit.runner.RunWith;
67 import org.mockito.ArgumentCaptor;
68 import org.mockito.InOrder;
69 import org.mockito.Mockito;
70 
71 /**
72  * Tests for the {@link WindowProcessController} class.
73  *
74  * Build/Install/Run:
75  *  atest WmTests:WindowProcessControllerTests
76  */
77 @Presubmit
78 @RunWith(WindowTestRunner.class)
79 public class WindowProcessControllerTests extends WindowTestsBase {
80 
81     WindowProcessController mWpc;
82     WindowProcessListener mMockListener;
83 
84     @Before
setUp()85     public void setUp() {
86         mMockListener = mock(WindowProcessListener.class);
87 
88         ApplicationInfo info = mock(ApplicationInfo.class);
89         info.packageName = "test.package.name";
90         mWpc = new WindowProcessController(
91                 mAtm, info, null, 0, -1, null, mMockListener);
92         mWpc.setThread(mock(IApplicationThread.class));
93     }
94 
95     @Test
testDisplayAreaConfigurationListener()96     public void testDisplayAreaConfigurationListener() {
97         // By default, the process should not listen to any display area.
98         assertNull(mWpc.getDisplayArea());
99 
100         // Register to ImeContainer on display 1 as a listener.
101         final TestDisplayContent testDisplayContent1 = createTestDisplayContentInContainer();
102         final DisplayArea imeContainer1 = testDisplayContent1.getImeContainer();
103         mWpc.registerDisplayAreaConfigurationListener(imeContainer1);
104         assertTrue(imeContainer1.containsListener(mWpc));
105         assertEquals(imeContainer1, mWpc.getDisplayArea());
106 
107         // Register to ImeContainer on display 2 as a listener.
108         final TestDisplayContent testDisplayContent2 = createTestDisplayContentInContainer();
109         final DisplayArea imeContainer2 = testDisplayContent2.getImeContainer();
110         mWpc.registerDisplayAreaConfigurationListener(imeContainer2);
111         assertFalse(imeContainer1.containsListener(mWpc));
112         assertTrue(imeContainer2.containsListener(mWpc));
113         assertEquals(imeContainer2, mWpc.getDisplayArea());
114 
115         // Null DisplayArea will not change anything.
116         mWpc.registerDisplayAreaConfigurationListener(null);
117         assertTrue(imeContainer2.containsListener(mWpc));
118         assertEquals(imeContainer2, mWpc.getDisplayArea());
119 
120         // Unregister listener will remove the wpc from registered display area.
121         mWpc.unregisterDisplayAreaConfigurationListener();
122         assertFalse(imeContainer1.containsListener(mWpc));
123         assertFalse(imeContainer2.containsListener(mWpc));
124         assertNull(mWpc.getDisplayArea());
125 
126         // Unregistration still work even if the display was removed.
127         mWpc.registerDisplayAreaConfigurationListener(imeContainer1);
128         assertEquals(imeContainer1, mWpc.getDisplayArea());
129         mRootWindowContainer.removeChild(testDisplayContent1);
130         mWpc.unregisterDisplayAreaConfigurationListener();
131         assertNull(mWpc.getDisplayArea());
132     }
133 
134     @Test
testDisplayAreaConfigurationListener_verifyConfig()135     public void testDisplayAreaConfigurationListener_verifyConfig() {
136         final Rect displayBounds = new Rect(0, 0, 2000, 1000);
137         final DisplayContent display = new TestDisplayContent.Builder(
138                 mAtm, displayBounds.width(), displayBounds.height())
139                 .setDensityDpi(300)
140                 .setPosition(DisplayContent.POSITION_TOP)
141                 .build();
142         final DisplayArea imeContainer = display.getImeContainer();
143 
144         // Register to the ime container.
145         mWpc.registerDisplayAreaConfigurationListener(imeContainer);
146 
147         assertEquals(displayBounds, mWpc.getConfiguration().windowConfiguration.getBounds());
148 
149         // Resize the ime container.
150         final Rect resizeImeBounds = new Rect(0, 0, 1000, 1000);
151         imeContainer.setBounds(resizeImeBounds);
152 
153         assertEquals(resizeImeBounds, mWpc.getConfiguration().windowConfiguration.getBounds());
154 
155         // Register to the display.
156         mWpc.registerDisplayAreaConfigurationListener(display);
157 
158         assertEquals(displayBounds, mWpc.getConfiguration().windowConfiguration.getBounds());
159     }
160 
161     @Test
testDestroy_unregistersDisplayAreaListener()162     public void testDestroy_unregistersDisplayAreaListener() {
163         final TestDisplayContent testDisplayContent1 = createTestDisplayContentInContainer();
164         final DisplayArea imeContainer1 = testDisplayContent1.getImeContainer();
165         mWpc.registerDisplayAreaConfigurationListener(imeContainer1);
166 
167         mWpc.destroy();
168 
169         assertNull(mWpc.getDisplayArea());
170     }
171 
172     @Test
testSetAnimatingReason()173     public void testSetAnimatingReason() {
174         mWpc.addAnimatingReason(WindowProcessController.ANIMATING_REASON_REMOTE_ANIMATION);
175         assertTrue(mWpc.isRunningRemoteTransition());
176         mWpc.addAnimatingReason(WindowProcessController.ANIMATING_REASON_WAKEFULNESS_CHANGE);
177         mWpc.removeAnimatingReason(WindowProcessController.ANIMATING_REASON_REMOTE_ANIMATION);
178         assertFalse(mWpc.isRunningRemoteTransition());
179         mWpc.removeAnimatingReason(WindowProcessController.ANIMATING_REASON_WAKEFULNESS_CHANGE);
180         waitHandlerIdle(mAtm.mH);
181 
182         InOrder orderVerifier = Mockito.inOrder(mMockListener);
183         orderVerifier.verify(mMockListener).setRunningRemoteAnimation(eq(true));
184         orderVerifier.verify(mMockListener).setRunningRemoteAnimation(eq(false));
185     }
186 
187     @Test
testSetRunningRemoteAnimation()188     public void testSetRunningRemoteAnimation() {
189         mWpc.setRunningRemoteAnimation(true);
190         mWpc.setRunningRemoteAnimation(false);
191         waitHandlerIdle(mAtm.mH);
192 
193         InOrder orderVerifier = Mockito.inOrder(mMockListener);
194         orderVerifier.verify(mMockListener).setRunningRemoteAnimation(eq(true));
195         orderVerifier.verify(mMockListener).setRunningRemoteAnimation(eq(false));
196     }
197 
198     @Test
testSetRunningBothAnimations()199     public void testSetRunningBothAnimations() {
200         mWpc.setRunningRemoteAnimation(true);
201         mWpc.setRunningRecentsAnimation(true);
202 
203         mWpc.setRunningRecentsAnimation(false);
204         mWpc.setRunningRemoteAnimation(false);
205         waitHandlerIdle(mAtm.mH);
206 
207         InOrder orderVerifier = Mockito.inOrder(mMockListener);
208         orderVerifier.verify(mMockListener, times(1)).setRunningRemoteAnimation(eq(true));
209         orderVerifier.verify(mMockListener, times(1)).setRunningRemoteAnimation(eq(false));
210         orderVerifier.verifyNoMoreInteractions();
211     }
212 
213     @Test
testConfigurationForSecondaryScreenDisplayArea()214     public void testConfigurationForSecondaryScreenDisplayArea() {
215         // By default, the process should not listen to any display area.
216         assertNull(mWpc.getDisplayArea());
217 
218         // Register to the ImeContainer on the new display as a listener.
219         final DisplayContent display = new TestDisplayContent.Builder(mAtm, 2000, 1000)
220                 .setDensityDpi(300).setPosition(DisplayContent.POSITION_TOP).build();
221         final DisplayArea imeContainer = display.getImeContainer();
222         mWpc.registerDisplayAreaConfigurationListener(imeContainer);
223 
224         assertEquals(imeContainer, mWpc.getDisplayArea());
225         final Configuration expectedConfig = mAtm.mRootWindowContainer.getConfiguration();
226         expectedConfig.updateFrom(imeContainer.getConfiguration());
227         assertEquals(expectedConfig, mWpc.getConfiguration());
228     }
229 
230     @Test
testActivityNotOverridingSystemUiProcessConfig()231     public void testActivityNotOverridingSystemUiProcessConfig() {
232         final ComponentName systemUiServiceComponent = mAtm.getSysUiServiceComponentLocked();
233         ApplicationInfo applicationInfo = mock(ApplicationInfo.class);
234         applicationInfo.packageName = systemUiServiceComponent.getPackageName();
235 
236         WindowProcessController wpc = new WindowProcessController(
237                 mAtm, applicationInfo, null, 0, -1, null, mMockListener);
238         wpc.setThread(mock(IApplicationThread.class));
239 
240         final ActivityRecord activity = createActivityRecord(wpc);
241         wpc.addActivityIfNeeded(activity);
242         // System UI owned processes should not be registered for activity config changes.
243         assertFalse(wpc.registeredForActivityConfigChanges());
244     }
245 
246     @Test
testActivityNotOverridingImeProcessConfig()247     public void testActivityNotOverridingImeProcessConfig() {
248         ServiceInfo serviceInfo = new ServiceInfo();
249         serviceInfo.permission = Manifest.permission.BIND_INPUT_METHOD;
250         // Notify WPC that this process has started an IME service.
251         mWpc.onServiceStarted(serviceInfo);
252 
253         final ActivityRecord activity = createActivityRecord(mWpc);
254         mWpc.addActivityIfNeeded(activity);
255         // IME processes should not be registered for activity config changes.
256         assertFalse(mWpc.registeredForActivityConfigChanges());
257     }
258 
259     @Test
testActivityNotOverridingAllyProcessConfig()260     public void testActivityNotOverridingAllyProcessConfig() {
261         ServiceInfo serviceInfo = new ServiceInfo();
262         serviceInfo.permission = Manifest.permission.BIND_ACCESSIBILITY_SERVICE;
263         // Notify WPC that this process has started an ally service.
264         mWpc.onServiceStarted(serviceInfo);
265 
266         final ActivityRecord activity = createActivityRecord(mWpc);
267         mWpc.addActivityIfNeeded(activity);
268         // Ally processes should not be registered for activity config changes.
269         assertFalse(mWpc.registeredForActivityConfigChanges());
270     }
271 
272     @Test
testActivityNotOverridingVoiceInteractionProcessConfig()273     public void testActivityNotOverridingVoiceInteractionProcessConfig() {
274         ServiceInfo serviceInfo = new ServiceInfo();
275         serviceInfo.permission = Manifest.permission.BIND_VOICE_INTERACTION;
276         // Notify WPC that this process has started an voice interaction service.
277         mWpc.onServiceStarted(serviceInfo);
278 
279         final ActivityRecord activity = createActivityRecord(mWpc);
280         mWpc.addActivityIfNeeded(activity);
281         // Voice interaction service processes should not be registered for activity config changes.
282         assertFalse(mWpc.registeredForActivityConfigChanges());
283     }
284 
285     @Test
testProcessLevelConfiguration()286     public void testProcessLevelConfiguration() {
287         Configuration config = new Configuration();
288         config.windowConfiguration.setActivityType(ACTIVITY_TYPE_HOME);
289         mWpc.onRequestedOverrideConfigurationChanged(config);
290         assertEquals(ACTIVITY_TYPE_HOME, config.windowConfiguration.getActivityType());
291         assertEquals(ACTIVITY_TYPE_UNDEFINED, mWpc.getActivityType());
292 
293         mWpc.onMergedOverrideConfigurationChanged(config);
294         assertEquals(ACTIVITY_TYPE_HOME, config.windowConfiguration.getActivityType());
295         assertEquals(ACTIVITY_TYPE_UNDEFINED, mWpc.getActivityType());
296 
297         final int globalSeq = 100;
298         mRootWindowContainer.getConfiguration().seq = globalSeq;
299         invertOrientation(mWpc.getConfiguration());
300         createActivityRecord(mWpc);
301 
302         assertTrue(mWpc.registeredForActivityConfigChanges());
303         assertEquals("Config seq of process should not be affected by activity",
304                 mWpc.getConfiguration().seq, globalSeq);
305     }
306 
307     @Test
testCachedStateConfigurationChange()308     public void testCachedStateConfigurationChange() throws RemoteException {
309         final ClientLifecycleManager clientManager = mAtm.getLifecycleManager();
310         doNothing().when(clientManager).scheduleTransaction(any(), any());
311         final IApplicationThread thread = mWpc.getThread();
312         final Configuration newConfig = new Configuration(mWpc.getConfiguration());
313         newConfig.densityDpi += 100;
314         // Non-cached state will send the change directly.
315         mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_IMPORTANT_BACKGROUND);
316         clearInvocations(clientManager);
317         mWpc.onConfigurationChanged(newConfig);
318         verify(clientManager).scheduleTransaction(eq(thread), any());
319 
320         // Cached state won't send the change.
321         clearInvocations(clientManager);
322         mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_CACHED_ACTIVITY);
323         newConfig.densityDpi += 100;
324         mWpc.onConfigurationChanged(newConfig);
325         verify(clientManager, never()).scheduleTransaction(eq(thread), any());
326 
327         // Cached -> non-cached will send the previous deferred config immediately.
328         mWpc.setReportedProcState(ActivityManager.PROCESS_STATE_RECEIVER);
329         final ArgumentCaptor<ConfigurationChangeItem> captor =
330                 ArgumentCaptor.forClass(ConfigurationChangeItem.class);
331         verify(clientManager).scheduleTransaction(eq(thread), captor.capture());
332         final ClientTransactionHandler client = mock(ClientTransactionHandler.class);
333         captor.getValue().preExecute(client, null /* token */);
334         final ArgumentCaptor<Configuration> configCaptor =
335                 ArgumentCaptor.forClass(Configuration.class);
336         verify(client).updatePendingConfiguration(configCaptor.capture());
337         assertEquals(newConfig, configCaptor.getValue());
338     }
339 
340     @Test
testComputeOomAdjFromActivities()341     public void testComputeOomAdjFromActivities() {
342         final ActivityRecord activity = createActivityRecord(mWpc);
343         activity.setVisibleRequested(true);
344         final int[] callbackResult = { 0 };
345         final int visible = 1;
346         final int paused = 2;
347         final int stopping = 4;
348         final int other = 8;
349         final WindowProcessController.ComputeOomAdjCallback callback =
350                 new WindowProcessController.ComputeOomAdjCallback() {
351             @Override
352             public void onVisibleActivity() {
353                 callbackResult[0] |= visible;
354             }
355 
356             @Override
357             public void onPausedActivity() {
358                 callbackResult[0] |= paused;
359             }
360 
361             @Override
362             public void onStoppingActivity(boolean finishing) {
363                 callbackResult[0] |= stopping;
364             }
365 
366             @Override
367             public void onOtherActivity() {
368                 callbackResult[0] |= other;
369             }
370         };
371 
372         // onStartActivity should refresh the state immediately.
373         mWpc.onStartActivity(0 /* topProcessState */, activity.info);
374         assertEquals(1 /* minTaskLayer */, mWpc.computeOomAdjFromActivities(callback));
375         assertEquals(visible, callbackResult[0]);
376 
377         callbackResult[0] = 0;
378         activity.setVisibleRequested(false);
379         activity.setState(PAUSED, "test");
380         mWpc.computeOomAdjFromActivities(callback);
381         assertEquals(paused, callbackResult[0]);
382 
383         callbackResult[0] = 0;
384         activity.setState(STOPPING, "test");
385         mWpc.computeOomAdjFromActivities(callback);
386         assertEquals(stopping, callbackResult[0]);
387 
388         callbackResult[0] = 0;
389         activity.setState(STOPPED, "test");
390         mWpc.computeOomAdjFromActivities(callback);
391         assertEquals(other, callbackResult[0]);
392     }
393 
394     @Test
testComputeProcessActivityState()395     public void testComputeProcessActivityState() {
396         final VisibleActivityProcessTracker tracker = mAtm.mVisibleActivityProcessTracker;
397         spyOn(tracker);
398         final ActivityRecord activity = createActivityRecord(mWpc);
399         activity.setVisibleRequested(true);
400         activity.setState(STARTED, "test");
401 
402         verify(tracker).onAnyActivityVisible(mWpc);
403         assertTrue(mWpc.hasVisibleActivities());
404 
405         activity.setState(RESUMED, "test");
406 
407         verify(tracker).onActivityResumedWhileVisible(mWpc);
408         assertTrue(tracker.hasResumedActivity(mWpc.mUid));
409 
410         activity.makeFinishingLocked();
411         activity.setState(PAUSING, "test");
412 
413         assertFalse(tracker.hasResumedActivity(mWpc.mUid));
414         assertTrue(mWpc.hasForegroundActivities());
415 
416         activity.setVisibility(false);
417         activity.setVisibleRequested(false);
418         activity.setState(STOPPED, "test");
419 
420         verify(tracker).onAllActivitiesInvisible(mWpc);
421         assertFalse(mWpc.hasVisibleActivities());
422         assertFalse(mWpc.hasForegroundActivities());
423     }
424 
createActivityRecord(WindowProcessController wpc)425     private ActivityRecord createActivityRecord(WindowProcessController wpc) {
426         return new ActivityBuilder(mAtm).setCreateTask(true).setUseProcess(wpc).build();
427     }
428 
429     @Test
testTopActivityUiModeChangeScheduleConfigChange()430     public void testTopActivityUiModeChangeScheduleConfigChange() {
431         final ActivityRecord activity = createActivityRecord(mWpc);
432         activity.setVisibleRequested(true);
433         doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any(), anyInt());
434         mWpc.updateAppSpecificSettingsForAllActivitiesInPackage(DEFAULT_COMPONENT_PACKAGE_NAME,
435                 Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"),
436                 GRAMMATICAL_GENDER_NOT_SPECIFIED);
437         verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean());
438     }
439 
440     @Test
testTopActivityUiModeChangeForDifferentPackage_noScheduledConfigChange()441     public void testTopActivityUiModeChangeForDifferentPackage_noScheduledConfigChange() {
442         final ActivityRecord activity = createActivityRecord(mWpc);
443         activity.setVisibleRequested(true);
444         mWpc.updateAppSpecificSettingsForAllActivitiesInPackage("com.different.package",
445                 Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"),
446                 GRAMMATICAL_GENDER_NOT_SPECIFIED);
447         verify(activity, never()).applyAppSpecificConfig(anyInt(), any(), anyInt());
448         verify(activity, never()).ensureActivityConfiguration(anyInt(), anyBoolean());
449     }
450 
451     @Test
testTopActivityDisplayAreaMatchesTopMostActivity_noActivities()452     public void testTopActivityDisplayAreaMatchesTopMostActivity_noActivities() {
453         assertNull(mWpc.getTopActivityDisplayArea());
454     }
455 
456     @Test
testTopActivityDisplayAreaMatchesTopMostActivity_singleActivity()457     public void testTopActivityDisplayAreaMatchesTopMostActivity_singleActivity() {
458         final ActivityRecord activityRecord = new ActivityBuilder(mSupervisor.mService).build();
459         final TaskDisplayArea expectedDisplayArea = mock(TaskDisplayArea.class);
460 
461         when(activityRecord.getDisplayArea())
462                 .thenReturn(expectedDisplayArea);
463 
464         mWpc.addActivityIfNeeded(activityRecord);
465 
466         assertEquals(expectedDisplayArea, mWpc.getTopActivityDisplayArea());
467     }
468 
469     /**
470      * Test that top most activity respects z-order.
471      */
472     @Test
testTopActivityDisplayAreaMatchesTopMostActivity_multipleActivities()473     public void testTopActivityDisplayAreaMatchesTopMostActivity_multipleActivities() {
474         final ActivityRecord bottomRecord = new ActivityBuilder(mSupervisor.mService).build();
475         final TaskDisplayArea bottomDisplayArea = mock(TaskDisplayArea.class);
476         final ActivityRecord topRecord = new ActivityBuilder(mSupervisor.mService).build();
477         final TaskDisplayArea topDisplayArea = mock(TaskDisplayArea.class);
478 
479         when(bottomRecord.getDisplayArea()).thenReturn(bottomDisplayArea);
480         when(topRecord.getDisplayArea()).thenReturn(topDisplayArea);
481         doReturn(-1).when(bottomRecord).compareTo(topRecord);
482         doReturn(1).when(topRecord).compareTo(bottomRecord);
483 
484         mWpc.addActivityIfNeeded(topRecord);
485         mWpc.addActivityIfNeeded(bottomRecord);
486 
487         assertEquals(topDisplayArea, mWpc.getTopActivityDisplayArea());
488     }
489 
createTestDisplayContentInContainer()490     private TestDisplayContent createTestDisplayContentInContainer() {
491         return new TestDisplayContent.Builder(mAtm, 1000, 1500).build();
492     }
493 
invertOrientation(Configuration config)494     private static void invertOrientation(Configuration config) {
495         config.orientation = config.orientation == ORIENTATION_PORTRAIT
496                 ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT;
497     }
498 }
499