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