1 /* 2 * Copyright (C) 2023 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 android.server.wm.jetpack.area; 18 19 import static android.server.wm.UiDeviceUtils.pressHomeButton; 20 import static android.server.wm.UiDeviceUtils.pressSleepButton; 21 import static android.server.wm.UiDeviceUtils.pressUnlockButton; 22 import static android.server.wm.UiDeviceUtils.pressWakeupButton; 23 import static android.view.Display.DEFAULT_DISPLAY; 24 25 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 26 import static androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_ACTIVE; 27 import static androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_CONTENT_VISIBLE; 28 import static androidx.window.extensions.area.WindowAreaComponent.SESSION_STATE_INACTIVE; 29 30 import static com.android.compatibility.common.util.PollingCheck.waitFor; 31 32 import static org.junit.Assert.assertEquals; 33 import static org.junit.Assert.assertFalse; 34 import static org.junit.Assert.assertNotEquals; 35 import static org.junit.Assert.assertNotNull; 36 import static org.junit.Assert.assertTrue; 37 import static org.junit.Assume.assumeTrue; 38 39 import android.app.ActivityManager; 40 import android.app.KeyguardManager; 41 import android.content.Context; 42 import android.content.res.Resources; 43 import android.hardware.devicestate.DeviceStateManager; 44 import android.hardware.devicestate.DeviceStateRequest; 45 import android.hardware.display.DisplayManager; 46 import android.os.PowerManager; 47 import android.platform.test.annotations.LargeTest; 48 import android.platform.test.annotations.Presubmit; 49 import android.server.wm.DeviceStateUtils; 50 import android.server.wm.jetpack.utils.ExtensionUtil; 51 import android.server.wm.jetpack.utils.TestActivity; 52 import android.server.wm.jetpack.utils.TestActivityLauncher; 53 import android.server.wm.jetpack.utils.TestRearDisplayActivity; 54 import android.server.wm.jetpack.utils.WindowExtensionTestRule; 55 import android.server.wm.jetpack.utils.WindowManagerJetpackTestBase; 56 import android.view.Display; 57 58 import androidx.test.ext.junit.runners.AndroidJUnit4; 59 import androidx.window.extensions.area.ExtensionWindowAreaPresentation; 60 import androidx.window.extensions.area.ExtensionWindowAreaStatus; 61 import androidx.window.extensions.area.WindowAreaComponent; 62 import androidx.window.extensions.area.WindowAreaComponent.WindowAreaStatus; 63 import androidx.window.extensions.core.util.function.Consumer; 64 65 import com.android.compatibility.common.util.ApiTest; 66 import com.android.compatibility.common.util.PollingCheck; 67 68 import org.junit.After; 69 import org.junit.Before; 70 import org.junit.Rule; 71 import org.junit.Test; 72 import org.junit.runner.RunWith; 73 74 import java.util.ArrayList; 75 import java.util.Arrays; 76 import java.util.HashSet; 77 import java.util.List; 78 import java.util.Objects; 79 import java.util.Set; 80 81 /** 82 * Tests for the {@link androidx.window.extensions.area.WindowAreaComponent} implementation 83 * of the rear display functionality provided on the device (and only if one is available). 84 * 85 * Build/Install/Run: 86 * atest CtsWindowManagerJetpackTestCases:ExtensionRearDisplayPresentationTest 87 */ 88 @LargeTest 89 @Presubmit 90 @RunWith(AndroidJUnit4.class) 91 public class ExtensionRearDisplayPresentationTest extends WindowManagerJetpackTestBase implements 92 DeviceStateManager.DeviceStateCallback { 93 94 private static final int TIMEOUT = 2000; 95 private static final int INVALID_DEVICE_STATE = -1; 96 97 private static final List<@WindowAreaComponent.WindowAreaSessionState Integer> 98 SESSION_LIFECYCLE_VALUES = new ArrayList<>( 99 Arrays.asList(SESSION_STATE_ACTIVE, SESSION_STATE_CONTENT_VISIBLE, 100 SESSION_STATE_ACTIVE, SESSION_STATE_INACTIVE)); 101 102 private TestRearDisplayActivity mActivity; 103 private int[] mFoldedDeviceStates; 104 private WindowAreaComponent mWindowAreaComponent; 105 private int mCurrentDeviceState; 106 private int mCurrentDeviceBaseState; 107 private int[] mSupportedDeviceStates; 108 private ExtensionWindowAreaStatus mWindowAreaPresentationStatus; 109 110 @WindowAreaComponent.WindowAreaSessionState 111 private int mWindowAreaSessionState; 112 private int mRearDisplayPresentationState; 113 114 private List<Integer> mSessionStateStatusValues; 115 116 private final Context mInstrumentationContext = getInstrumentation().getTargetContext(); 117 private final KeyguardManager mKeyguardManager = mInstrumentationContext.getSystemService( 118 KeyguardManager.class); 119 private final DeviceStateManager mDeviceStateManager = mInstrumentationContext 120 .getSystemService(DeviceStateManager.class); 121 private final DisplayManager mDisplayManager = mInstrumentationContext 122 .getSystemService(DisplayManager.class); 123 private final ActivityManager mActivityManager = mInstrumentationContext 124 .getSystemService(ActivityManager.class); 125 126 private final Consumer<ExtensionWindowAreaStatus> mStatusListener = 127 (status) -> mWindowAreaPresentationStatus = status; 128 129 private final Consumer<@WindowAreaComponent.WindowAreaSessionState Integer> 130 mSessionStateListener = (sessionStatus) -> { 131 mSessionStateStatusValues.add(sessionStatus); 132 mWindowAreaSessionState = sessionStatus; 133 }; 134 135 @Rule 136 public final WindowExtensionTestRule mWindowManagerJetpackTestRule = 137 new WindowExtensionTestRule(WindowAreaComponent.class); 138 139 @Before 140 @Override setUp()141 public void setUp() { 142 super.setUp(); 143 mSessionStateStatusValues = new ArrayList<>(); 144 mSupportedDeviceStates = mDeviceStateManager.getSupportedStates(); 145 assumeTrue(mSupportedDeviceStates.length > 1); 146 mFoldedDeviceStates = getInstrumentation().getTargetContext().getResources().getIntArray( 147 Resources.getSystem().getIdentifier("config_foldedDeviceStates", "array", 148 "android")); 149 assumeTrue(mFoldedDeviceStates.length > 0); 150 // TODO(b/236022708) Move rear display presentation state to device state config file 151 mRearDisplayPresentationState = getInstrumentation().getTargetContext().getResources() 152 .getInteger(Resources.getSystem().getIdentifier( 153 "config_deviceStateConcurrentRearDisplay", "integer", "android")); 154 assumeTrue(mRearDisplayPresentationState != INVALID_DEVICE_STATE); 155 assumeTrue(containsValue(mSupportedDeviceStates, mRearDisplayPresentationState)); 156 String rearDisplayAddress = getInstrumentation().getTargetContext().getResources() 157 .getString(Resources.getSystem().getIdentifier( 158 "config_rearDisplayPhysicalAddress", "string", "android")); 159 assumeTrue(rearDisplayAddress != null && !rearDisplayAddress.isEmpty()); 160 mDeviceStateManager.registerCallback(Runnable::run, this); 161 mWindowAreaComponent = 162 (WindowAreaComponent) mWindowManagerJetpackTestRule.getExtensionComponent(); 163 mWindowAreaComponent.addRearDisplayPresentationStatusListener(mStatusListener); 164 unlockDeviceIfNeeded(); 165 mActivity = startActivityNewTask(TestRearDisplayActivity.class); 166 waitAndAssert(() -> mWindowAreaPresentationStatus != null); 167 } 168 169 @After 170 @Override tearDown()171 public void tearDown() { 172 super.tearDown(); 173 mDeviceStateManager.unregisterCallback(this); 174 if (mWindowAreaComponent != null) { 175 mWindowAreaComponent.removeRearDisplayPresentationStatusListener(mStatusListener); 176 try { 177 DeviceStateUtils.runWithControlDeviceStatePermission( 178 mDeviceStateManager::cancelStateRequest); 179 DeviceStateUtils.runWithControlDeviceStatePermission( 180 mDeviceStateManager::cancelBaseStateOverride); 181 } catch (Throwable t) { 182 throw new RuntimeException(t); 183 } 184 } 185 } 186 187 /** 188 * Tests that the RearDisplayPresentation status listeners receive the correct 189 * {@link WindowAreaStatus} values. 190 * 191 * The test goes through all supported device states and verifies that the correct status is 192 * returned. If the state does not place the device in the active RearDisplayPresentation state 193 * and the state is not marked as `folded`, then it should receive the 194 * {@link WindowAreaStatus#STATUS_AVAILABLE} value, otherwise it should receive the 195 * {@link WindowAreaStatus#STATUS_UNAVAILABLE} value. 196 */ 197 @ApiTest(apis = { 198 "androidx.window.extensions.area." 199 + "WindowAreaComponent#addRearDisplayPresentationStatusListener", 200 "androidx.window.extensions.area." 201 + "WindowAreaComponent#removeRearDisplayPresentationStatusListener"}) 202 @Test testRearDisplayPresentationStatusListeners()203 public void testRearDisplayPresentationStatusListeners() throws Throwable { 204 Set<Integer> requestedStates = new HashSet<>(); 205 while (requestedStates.size() != mSupportedDeviceStates.length) { 206 int newState = determineNewState(mCurrentDeviceState, mSupportedDeviceStates, 207 requestedStates); 208 if (newState != INVALID_DEVICE_STATE) { 209 requestedStates.add(newState); 210 DeviceStateRequest request = DeviceStateRequest.newBuilder(newState).build(); 211 DeviceStateUtils.runWithControlDeviceStatePermission(() -> 212 mDeviceStateManager.requestState(request, null, null)); 213 214 waitAndAssert(() -> mCurrentDeviceState == newState); 215 216 // If the state does not put the device into the rear display presentation state, 217 // and the state is not one where the device is folded, the status should be 218 // available. 219 if (ExtensionUtil.getWindowExtensions().getVendorApiLevel() >= 4 220 && mCurrentDeviceState == mRearDisplayPresentationState) { 221 waitAndAssert(() -> mWindowAreaPresentationStatus.getWindowAreaStatus() 222 == WindowAreaComponent.STATUS_ACTIVE); 223 } else if (containsValue(mFoldedDeviceStates, mCurrentDeviceState)) { 224 waitAndAssert(() -> mWindowAreaPresentationStatus.getWindowAreaStatus() 225 == WindowAreaComponent.STATUS_UNAVAILABLE); 226 } else { 227 waitAndAssert(() -> mWindowAreaPresentationStatus.getWindowAreaStatus() 228 == WindowAreaComponent.STATUS_AVAILABLE); 229 } 230 } 231 } 232 } 233 234 /** 235 * Tests that you can start and end rear display presentation mode with 236 * {@link WindowAreaComponent#endRearDisplayPresentationSession()}. Verifies that the 237 * {@link Consumer} that is provided when calling 238 * {@link WindowAreaComponent#startRearDisplayPresentationSession} receives the 239 * {@link WindowAreaComponent#SESSION_STATE_ACTIVE when starting the session and 240 * {@link WindowAreaComponent#SESSION_STATE_INACTIVE} when calling 241 * {@link WindowAreaComponent#endRearDisplayPresentationSession()}. 242 * 243 * This test also verifies that the {@link TestPresentationView} was attached to the window 244 * signifying that the presentation was created as expected, and then removed and detached as 245 * expected. 246 */ 247 @ApiTest(apis = { 248 "androidx.window.extensions.area." 249 + "WindowAreaComponent#startRearDisplayPresentationSession", 250 "androidx.window.extensions.area." 251 + "WindowAreaComponent#endRearDisplayPresentationSession"}) 252 @Test testStartAndEndRearDisplayPresentationSession()253 public void testStartAndEndRearDisplayPresentationSession() throws Throwable { 254 assumeTrue(mWindowAreaPresentationStatus.getWindowAreaStatus() 255 == WindowAreaComponent.STATUS_AVAILABLE); 256 assumeTrue(mCurrentDeviceState != mRearDisplayPresentationState); 257 258 // Rear displays should only exist after concurrent mode is started 259 assertEquals(0, mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR).length); 260 261 mWindowAreaComponent.startRearDisplayPresentationSession(mActivity, 262 mSessionStateListener); 263 waitAndAssert(() -> mWindowAreaSessionState == SESSION_STATE_ACTIVE); 264 assertEquals(mCurrentDeviceState, mRearDisplayPresentationState); 265 assertTrue(mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR).length > 0); 266 267 ExtensionWindowAreaPresentation presentation = 268 mWindowAreaComponent.getRearDisplayPresentation(); 269 assertNotNull(presentation); 270 TestPresentationView presentationView = new TestPresentationView( 271 presentation.getPresentationContext()); 272 mActivity.runOnUiThread(() -> presentation.setPresentationView(presentationView)); 273 waitAndAssert(() -> presentationView.mAttachedToWindow); 274 assertNotEquals(presentationView.getDisplay().getDisplayId(), DEFAULT_DISPLAY); 275 assertTrue(presentationView.getDisplay().getState() != Display.STATE_OFF); 276 assertEquals(mWindowAreaSessionState, SESSION_STATE_CONTENT_VISIBLE); 277 278 mWindowAreaComponent.endRearDisplayPresentationSession(); 279 waitAndAssert(() -> !presentationView.mAttachedToWindow); 280 // Cancelling rear display presentation mode should cancel the override, so verifying that 281 // the device state is the same as the physical state of the device. 282 assertEquals(mCurrentDeviceState, mCurrentDeviceBaseState); 283 assertEquals(WindowAreaComponent.STATUS_AVAILABLE, 284 (int) mWindowAreaPresentationStatus.getWindowAreaStatus()); 285 // Since the non-visible and session ended callbacks happen so fast, we check if 286 // the list of values received equal what we expected. 287 assertEquals(mSessionStateStatusValues, SESSION_LIFECYCLE_VALUES); 288 } 289 290 /** 291 * Tests that you can start, and then end rear display presentation mode by backgrounding the 292 * calling application. Verifies that the {@link ExtensionWindowAreaPresentationSessionCallback} 293 * that is provided when calling {@link WindowAreaComponent#startRearDisplayPresentationSession} 294 * receives the {@link ExtensionWindowAreaPresentationSessionCallback#onSessionStarted} callback 295 * when starting the session and 296 * {@link ExtensionWindowAreaPresentationSessionCallback#onSessionEnded()} when backgrounding 297 * the application. 298 * 299 * This test also verifies that the {@link TestPresentationView} was attached to the window 300 * signifying that the presentation was created as expected, and then removed and detached as 301 * expected. 302 */ 303 @ApiTest(apis = { 304 "androidx.window.extensions.area." 305 + "WindowAreaComponent#startRearDisplayPresentationSession", 306 "androidx.window.extensions.area." 307 + "WindowAreaComponent#endRearDisplayPresentationSession"}) 308 @Test testStartAndEndRearDisplayPresentationSession_backgroundApp()309 public void testStartAndEndRearDisplayPresentationSession_backgroundApp() throws Throwable { 310 assumeTrue(mWindowAreaPresentationStatus.getWindowAreaStatus() 311 == WindowAreaComponent.STATUS_AVAILABLE); 312 assumeTrue(mCurrentDeviceState != mRearDisplayPresentationState); 313 314 mWindowAreaComponent.startRearDisplayPresentationSession(mActivity, 315 mSessionStateListener); 316 waitAndAssert(() -> SESSION_STATE_ACTIVE == mWindowAreaSessionState); 317 waitAndAssert(() -> mCurrentDeviceState == mRearDisplayPresentationState); 318 319 ExtensionWindowAreaPresentation presentation = 320 mWindowAreaComponent.getRearDisplayPresentation(); 321 assertNotNull(presentation); 322 TestPresentationView presentationView = new TestPresentationView( 323 presentation.getPresentationContext()); 324 mActivity.runOnUiThread(() -> presentation.setPresentationView(presentationView)); 325 waitAndAssert(() -> presentationView.mAttachedToWindow); 326 waitAndAssert(() -> presentationView.getDisplay().getState() != Display.STATE_OFF); 327 assertNotEquals(presentationView.getDisplay().getDisplayId(), DEFAULT_DISPLAY); 328 assertEquals(mWindowAreaSessionState, SESSION_STATE_CONTENT_VISIBLE); 329 330 pressHomeButton(); 331 waitAndAssert(() -> !presentationView.mAttachedToWindow); 332 // Cancelling rear display presentation mode should cancel the override, so verifying that 333 // the device state is the same as the physical state of the device. 334 assertEquals(mCurrentDeviceState, mCurrentDeviceBaseState); 335 assertEquals(WindowAreaComponent.STATUS_AVAILABLE, 336 (int) mWindowAreaPresentationStatus.getWindowAreaStatus()); 337 // Since the non-visible and session ended callbacks happen so fast, we check if 338 // the list of values received equal what we expected. 339 assertEquals(mSessionStateStatusValues, SESSION_LIFECYCLE_VALUES); 340 } 341 342 /** 343 * Tests that you can start, and then end rear display presentation mode by locking the device. 344 * Verifies that the {@link ExtensionWindowAreaPresentationSessionCallback} that is 345 * provided when calling {@link WindowAreaComponent#startRearDisplayPresentationSession} 346 * receives the {@link ExtensionWindowAreaPresentationSessionCallback#onSessionStarted} callback 347 * when starting the session and 348 * {@link ExtensionWindowAreaPresentationSessionCallback#onSessionEnded()} when locking the 349 * device. 350 * 351 * This test also verifies that the {@link TestPresentationView} was attached to the window 352 * signifying that the presentation was created as expected, and then removed and detached as 353 * expected. 354 */ 355 @ApiTest(apis = { 356 "androidx.window.extensions.area." 357 + "WindowAreaComponent#startRearDisplayPresentationSession", 358 "androidx.window.extensions.area." 359 + "WindowAreaComponent#endRearDisplayPresentationSession"}) 360 @Test testStartAndEndRearDisplayPresentationSession_lockDevice()361 public void testStartAndEndRearDisplayPresentationSession_lockDevice() throws Throwable { 362 assumeTrue(mWindowAreaPresentationStatus.getWindowAreaStatus() 363 == WindowAreaComponent.STATUS_AVAILABLE); 364 assumeTrue(mCurrentDeviceState != mRearDisplayPresentationState); 365 366 mWindowAreaComponent.startRearDisplayPresentationSession(mActivity, 367 mSessionStateListener); 368 waitAndAssert(() -> mWindowAreaSessionState == SESSION_STATE_ACTIVE); 369 assertEquals(mCurrentDeviceState, mRearDisplayPresentationState); 370 371 ExtensionWindowAreaPresentation presentation = 372 mWindowAreaComponent.getRearDisplayPresentation(); 373 assertNotNull(presentation); 374 TestPresentationView presentationView = new TestPresentationView( 375 presentation.getPresentationContext()); 376 mActivity.runOnUiThread(() -> presentation.setPresentationView(presentationView)); 377 waitAndAssert(() -> presentationView.mAttachedToWindow); 378 assertNotEquals(presentationView.getDisplay().getDisplayId(), DEFAULT_DISPLAY); 379 assertTrue(presentationView.getDisplay().getState() != Display.STATE_OFF); 380 assertEquals(mWindowAreaSessionState, SESSION_STATE_CONTENT_VISIBLE); 381 382 pressSleepButton(); 383 waitAndAssert(() -> !presentationView.mAttachedToWindow); 384 // Cancelling rear display presentation mode should cancel the override, so verifying that 385 // the device state is the same as the physical state of the device. 386 assertEquals(mCurrentDeviceState, mCurrentDeviceBaseState); 387 assertEquals(WindowAreaComponent.STATUS_AVAILABLE, 388 (int) mWindowAreaPresentationStatus.getWindowAreaStatus()); 389 // Since the non-visible and session ended callbacks happen so fast, we check if 390 // the list of values received equal what we expected. 391 assertEquals(mSessionStateStatusValues, SESSION_LIFECYCLE_VALUES); 392 } 393 394 @ApiTest(apis = { 395 "androidx.window.extensions.area." 396 + "WindowAreaComponent#startRearDisplayPresentationSession", 397 "androidx.window.extensions.area." 398 + "WindowAreaComponent#endRearDisplayPresentationSession"}) 399 @Test (expected = SecurityException.class) testStartActivityOnRearDisplay_whileRearPresentationSessionStarted()400 public void testStartActivityOnRearDisplay_whileRearPresentationSessionStarted() 401 throws Throwable { 402 assumeTrue(mWindowAreaPresentationStatus.getWindowAreaStatus() 403 == WindowAreaComponent.STATUS_AVAILABLE); 404 assumeTrue(mCurrentDeviceState != mRearDisplayPresentationState); 405 406 mWindowAreaComponent.startRearDisplayPresentationSession(mActivity, 407 mSessionStateListener); 408 waitAndAssert(() -> mWindowAreaSessionState == SESSION_STATE_ACTIVE); 409 assertEquals(mCurrentDeviceState, mRearDisplayPresentationState); 410 411 Display[] rearDisplays = mDisplayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_REAR); 412 assertTrue(rearDisplays.length > 0); 413 414 final int rearDisplayId = rearDisplays[0].getDisplayId(); 415 416 final TestActivityLauncher<TestActivity> launcher = 417 launcherForNewActivity(TestActivity.class, rearDisplayId); 418 419 final boolean allowed = mActivityManager.isActivityStartAllowedOnDisplay( 420 mInstrumentationContext, rearDisplayId, launcher.getIntent()); 421 assertFalse("Should not be allowed to launch", allowed); 422 423 // Should throw SecurityException 424 launcher.launch(mInstrumentation); 425 } 426 427 /** 428 * Tests that the system properly cleans up the rear display presentation if an activity that 429 * started it finished without cleaning itself up. 430 */ 431 @ApiTest(apis = { 432 "androidx.window.extensions.area." 433 + "WindowAreaComponent#addRearDisplayPresentationStatusListener"}) 434 @Test testStartRearDisplayPresentation_applicationFinishes()435 public void testStartRearDisplayPresentation_applicationFinishes() { 436 assumeTrue(mWindowAreaPresentationStatus.getWindowAreaStatus() 437 == WindowAreaComponent.STATUS_AVAILABLE); 438 assumeTrue(mCurrentDeviceState != mRearDisplayPresentationState); 439 440 mWindowAreaComponent.startRearDisplayPresentationSession(mActivity, 441 mSessionStateListener); 442 443 waitAndAssert(() -> mWindowAreaSessionState == SESSION_STATE_ACTIVE); 444 assertEquals(mRearDisplayPresentationState, mCurrentDeviceState); 445 446 ExtensionWindowAreaPresentation presentation = 447 mWindowAreaComponent.getRearDisplayPresentation(); 448 TestPresentationView presentationView = new TestPresentationView( 449 presentation.getPresentationContext()); 450 mActivity.runOnUiThread(() -> 451 presentation.setPresentationView(presentationView)); 452 waitAndAssert(() -> presentationView.mAttachedToWindow); 453 454 mActivity.finish(); 455 456 waitAndAssert(() -> mWindowAreaSessionState == SESSION_STATE_INACTIVE); 457 458 // Currently when ending rear display presentation session, the display turns off. If this 459 // expectation ever changes, we will probably also need to update KeyguardPresentation in 460 // SystemUI to ensure that the secondary keyguard is shown. 461 waitAndAssert(() -> Display.STATE_ON 462 != presentation.getPresentationContext().getDisplay().getState()); 463 } 464 465 /** 466 * Tests that an app in the background cannot start a rear display presentation session. 467 */ 468 @ApiTest(apis = { 469 "androidx.window.extensions.area." 470 + "WindowAreaComponent#startRearDisplayPresentationSession"}) 471 @Test (expected = SecurityException.class) testStartRearDisplayPresentation_whenInBackground()472 public void testStartRearDisplayPresentation_whenInBackground() { 473 assumeTrue(mWindowAreaPresentationStatus.getWindowAreaStatus() 474 == WindowAreaComponent.STATUS_AVAILABLE); 475 assumeTrue(mCurrentDeviceState != mRearDisplayPresentationState); 476 477 pressHomeButton(); 478 waitAndAssert(() -> mActivity.onStopInvoked); 479 480 mWindowAreaComponent.startRearDisplayPresentationSession(mActivity, 481 mSessionStateListener); 482 } 483 484 485 @Override onBaseStateChanged(int state)486 public void onBaseStateChanged(int state) { 487 mCurrentDeviceBaseState = state; 488 } 489 490 @Override onStateChanged(int state)491 public void onStateChanged(int state) { 492 mCurrentDeviceState = state; 493 } 494 495 /** 496 * Returns the next state that we should request that isn't the current state and 497 * has not already been requested. 498 */ determineNewState(int currentDeviceState, int[] statesToRequest, Set<Integer> requestedStates)499 private int determineNewState(int currentDeviceState, int[] statesToRequest, 500 Set<Integer> requestedStates) { 501 for (int state : statesToRequest) { 502 if (state != currentDeviceState && !requestedStates.contains(state)) { 503 return state; 504 } 505 } 506 return INVALID_DEVICE_STATE; 507 } 508 containsValue(int[] values, int value)509 private boolean containsValue(int[] values, int value) { 510 for (int i = 0; i < values.length; i++) { 511 if (values[i] == value) return true; 512 } 513 return false; 514 } 515 unlockDeviceIfNeeded()516 private void unlockDeviceIfNeeded() { 517 if (isKeyguardLocked() || !Objects.requireNonNull( 518 mInstrumentationContext.getSystemService(PowerManager.class)).isInteractive()) { 519 pressWakeupButton(); 520 pressUnlockButton(); 521 } 522 } 523 isKeyguardLocked()524 private boolean isKeyguardLocked() { 525 return mKeyguardManager != null && mKeyguardManager.isKeyguardLocked(); 526 } 527 waitAndAssert(PollingCheck.PollingCheckCondition condition)528 private void waitAndAssert(PollingCheck.PollingCheckCondition condition) { 529 waitFor(TIMEOUT, condition); 530 } 531 } 532