• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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