• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.server.wm;
18 
19 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
20 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
21 import static android.server.wm.ComponentNameUtils.getActivityName;
22 import static android.server.wm.StateLogger.log;
23 import static android.server.wm.StateLogger.logAlways;
24 import static android.server.wm.UiDeviceUtils.pressSleepButton;
25 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
26 import static android.server.wm.app.Components.VIRTUAL_DISPLAY_ACTIVITY;
27 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_CREATE_DISPLAY;
28 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_DESTROY_DISPLAY;
29 import static android.server.wm.app.Components.VirtualDisplayActivity.COMMAND_RESIZE_DISPLAY;
30 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD;
31 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_COMMAND;
32 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_COUNT;
33 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_DENSITY_DPI;
34 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_LAUNCH_TARGET_COMPONENT;
35 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PRESENTATION_DISPLAY;
36 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
37 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
38 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_SHOW_SYSTEM_DECORATIONS;
39 import static android.server.wm.app.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
40 import static android.view.Display.DEFAULT_DISPLAY;
41 import static android.view.Display.INVALID_DISPLAY;
42 
43 import static androidx.test.InstrumentationRegistry.getInstrumentation;
44 
45 import static org.hamcrest.MatcherAssert.assertThat;
46 import static org.hamcrest.Matchers.hasSize;
47 import static org.junit.Assert.assertEquals;
48 import static org.junit.Assert.assertTrue;
49 import static org.junit.Assert.fail;
50 
51 import android.content.ComponentName;
52 import android.content.Context;
53 import android.content.res.Configuration;
54 import android.graphics.Rect;
55 import android.os.Bundle;
56 import android.os.SystemClock;
57 import android.provider.Settings;
58 import android.server.wm.ActivityManagerState.ActivityDisplay;
59 import android.server.wm.CommandSession.ActivitySession;
60 import android.server.wm.CommandSession.ActivitySessionClient;
61 import android.server.wm.settings.SettingsSession;
62 import android.util.Size;
63 import android.util.SparseBooleanArray;
64 import android.view.WindowManager;
65 
66 import androidx.annotation.NonNull;
67 import androidx.annotation.Nullable;
68 
69 import com.android.compatibility.common.util.SystemUtil;
70 import com.android.compatibility.common.util.TestUtils;
71 import org.junit.Before;
72 
73 import java.util.ArrayList;
74 import java.util.Collections;
75 import java.util.List;
76 import java.util.function.Consumer;
77 import java.util.regex.Matcher;
78 import java.util.regex.Pattern;
79 
80 /**
81  * Base class for ActivityManager display tests.
82  *
83  * @see DisplayTests
84  * @see MultiDisplayKeyguardTests
85  * @see MultiDisplayLockedKeyguardTests
86  */
87 public class MultiDisplayTestBase extends ActivityManagerTestBase {
88 
89     static final int CUSTOM_DENSITY_DPI = 222;
90     private static final int INVALID_DENSITY_DPI = -1;
91     protected Context mTargetContext;
92 
93     @Before
94     @Override
setUp()95     public void setUp() throws Exception {
96         super.setUp();
97         mTargetContext = getInstrumentation().getTargetContext();
98     }
99 
getDisplayState(int displayId)100     ActivityDisplay getDisplayState(int displayId) {
101         return getDisplayState(getDisplaysStates(), displayId);
102     }
103 
getDisplayState(List<ActivityDisplay> displays, int displayId)104     ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int displayId) {
105         for (ActivityDisplay display : displays) {
106             if (display.mId == displayId) {
107                 return display;
108             }
109         }
110         return null;
111     }
112 
113     /** Return the display state with width, height, dpi. Always not default display. */
getDisplayState(List<ActivityDisplay> displays, int width, int height, int dpi)114     ActivityDisplay getDisplayState(List<ActivityDisplay> displays, int width, int height,
115             int dpi) {
116         for (ActivityDisplay display : displays) {
117             if (display.mId == DEFAULT_DISPLAY) {
118                 continue;
119             }
120             final Configuration config = display.mFullConfiguration;
121             if (config.densityDpi == dpi && config.screenWidthDp == width
122                     && config.screenHeightDp == height) {
123                 return display;
124             }
125         }
126         return null;
127     }
128 
getDisplaysStates()129     List<ActivityDisplay> getDisplaysStates() {
130         mAmWmState.getAmState().computeState();
131         return mAmWmState.getAmState().getDisplays();
132     }
133 
134     /** Find the display that was not originally reported in oldDisplays and added in newDisplays */
findNewDisplayStates(List<ActivityDisplay> oldDisplays, List<ActivityDisplay> newDisplays)135     List<ActivityDisplay> findNewDisplayStates(List<ActivityDisplay> oldDisplays,
136             List<ActivityDisplay> newDisplays) {
137         final ArrayList<ActivityDisplay> result = new ArrayList<>();
138 
139         for (ActivityDisplay newDisplay : newDisplays) {
140             if (oldDisplays.stream().noneMatch(d -> d.mId == newDisplay.mId)) {
141                 result.add(newDisplay);
142             }
143         }
144 
145         return result;
146     }
147 
148     public static class ReportedDisplayMetrics {
149         private static final String WM_SIZE = "wm size";
150         private static final String WM_DENSITY = "wm density";
151         private static final Pattern PHYSICAL_SIZE =
152                 Pattern.compile("Physical size: (\\d+)x(\\d+)");
153         private static final Pattern OVERRIDE_SIZE =
154                 Pattern.compile("Override size: (\\d+)x(\\d+)");
155         private static final Pattern PHYSICAL_DENSITY =
156                 Pattern.compile("Physical density: (\\d+)");
157         private static final Pattern OVERRIDE_DENSITY =
158                 Pattern.compile("Override density: (\\d+)");
159 
160         @NonNull
161         final Size physicalSize;
162         final int physicalDensity;
163 
164         @Nullable
165         final Size overrideSize;
166         @Nullable
167         final Integer overrideDensity;
168 
169         /** Get physical and override display metrics from WM for specified display. */
getDisplayMetrics(int displayId)170         public static ReportedDisplayMetrics getDisplayMetrics(int displayId) {
171             return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId)
172                             + executeShellCommand(WM_DENSITY + " -d " + displayId));
173         }
174 
setDisplayMetrics(final Size size, final int density)175         void setDisplayMetrics(final Size size, final int density) {
176             setSize(size);
177             setDensity(density);
178         }
179 
restoreDisplayMetrics()180         void restoreDisplayMetrics() {
181             if (overrideSize != null) {
182                 setSize(overrideSize);
183             } else {
184                 executeShellCommand(WM_SIZE + " reset");
185             }
186             if (overrideDensity != null) {
187                 setDensity(overrideDensity);
188             } else {
189                 executeShellCommand(WM_DENSITY + " reset");
190             }
191         }
192 
setSize(final Size size)193         private void setSize(final Size size) {
194             executeShellCommand(WM_SIZE + " " + size.getWidth() + "x" + size.getHeight());
195         }
196 
setDensity(final int density)197         private void setDensity(final int density) {
198             executeShellCommand(WM_DENSITY + " " + density);
199         }
200 
201         /** Get display size that WM operates with. */
getSize()202         public Size getSize() {
203             return overrideSize != null ? overrideSize : physicalSize;
204         }
205 
206         /** Get density that WM operates with. */
getDensity()207         int getDensity() {
208             return overrideDensity != null ? overrideDensity : physicalDensity;
209         }
210 
ReportedDisplayMetrics(final String lines)211         private ReportedDisplayMetrics(final String lines) {
212             Matcher matcher = PHYSICAL_SIZE.matcher(lines);
213             assertTrue("Physical display size must be reported", matcher.find());
214             log(matcher.group());
215             physicalSize = new Size(
216                     Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
217 
218             matcher = PHYSICAL_DENSITY.matcher(lines);
219             assertTrue("Physical display density must be reported", matcher.find());
220             log(matcher.group());
221             physicalDensity = Integer.parseInt(matcher.group(1));
222 
223             matcher = OVERRIDE_SIZE.matcher(lines);
224             if (matcher.find()) {
225                 log(matcher.group());
226                 overrideSize = new Size(
227                         Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
228             } else {
229                 overrideSize = null;
230             }
231 
232             matcher = OVERRIDE_DENSITY.matcher(lines);
233             if (matcher.find()) {
234                 log(matcher.group());
235                 overrideDensity = Integer.parseInt(matcher.group(1));
236             } else {
237                 overrideDensity = null;
238             }
239         }
240     }
241 
tapOnDisplayCenter(int displayId)242     protected void tapOnDisplayCenter(int displayId) {
243         final Rect bounds = mAmWmState.getWmState().getDisplay(displayId).getDisplayRect();
244         tapOnDisplay(bounds.centerX(), bounds.centerY(), displayId);
245     }
246 
247     /**
248      * This class should only be used when you need to test virtual display created by a
249      * non-privileged app.
250      * Or when you need to test on simulated display.
251      *
252      * If you need to test virtual display created by a privileged app, please use
253      * {@link ExternalDisplaySession} instead.
254      */
255     public class VirtualDisplaySession implements AutoCloseable {
256         private int mDensityDpi = CUSTOM_DENSITY_DPI;
257         private boolean mLaunchInSplitScreen = false;
258         private boolean mCanShowWithInsecureKeyguard = false;
259         private boolean mPublicDisplay = false;
260         private boolean mResizeDisplay = true;
261         private boolean mShowSystemDecorations = false;
262         private boolean mPresentationDisplay = false;
263         private ComponentName mLaunchActivity = null;
264         private boolean mSimulateDisplay = false;
265         private boolean mMustBeCreated = true;
266         private Size mSimulationDisplaySize = new Size(1024 /* width */, 768 /* height */);
267 
268         private boolean mVirtualDisplayCreated = false;
269         private final OverlayDisplayDevicesSession mOverlayDisplayDeviceSession =
270                 new OverlayDisplayDevicesSession(mContext);
271 
setDensityDpi(int densityDpi)272         VirtualDisplaySession setDensityDpi(int densityDpi) {
273             mDensityDpi = densityDpi;
274             return this;
275         }
276 
setLaunchInSplitScreen(boolean launchInSplitScreen)277         VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
278             mLaunchInSplitScreen = launchInSplitScreen;
279             return this;
280         }
281 
setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)282         VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
283             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
284             return this;
285         }
286 
setPublicDisplay(boolean publicDisplay)287         VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
288             mPublicDisplay = publicDisplay;
289             return this;
290         }
291 
setResizeDisplay(boolean resizeDisplay)292         VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
293             mResizeDisplay = resizeDisplay;
294             return this;
295         }
296 
setShowSystemDecorations(boolean showSystemDecorations)297         VirtualDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
298             mShowSystemDecorations = showSystemDecorations;
299             return this;
300         }
301 
setPresentationDisplay(boolean presentationDisplay)302         VirtualDisplaySession setPresentationDisplay(boolean presentationDisplay) {
303             mPresentationDisplay = presentationDisplay;
304             return this;
305         }
306 
setLaunchActivity(ComponentName launchActivity)307         VirtualDisplaySession setLaunchActivity(ComponentName launchActivity) {
308             mLaunchActivity = launchActivity;
309             return this;
310         }
311 
setSimulateDisplay(boolean simulateDisplay)312         public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
313             mSimulateDisplay = simulateDisplay;
314             return this;
315         }
316 
setSimulationDisplaySize(int width, int height)317         VirtualDisplaySession setSimulationDisplaySize(int width, int height) {
318             mSimulationDisplaySize = new Size(width, height);
319             return this;
320         }
321 
setMustBeCreated(boolean mustBeCreated)322         VirtualDisplaySession setMustBeCreated(boolean mustBeCreated) {
323             mMustBeCreated = mustBeCreated;
324             return this;
325         }
326 
327         @Nullable
createDisplay()328         public ActivityDisplay createDisplay() throws Exception {
329             return createDisplays(1).stream().findFirst().orElse(null);
330         }
331 
332         @NonNull
createDisplays(int count)333         List<ActivityDisplay> createDisplays(int count) throws Exception {
334             if (mSimulateDisplay) {
335                 return simulateDisplay();
336             } else {
337                 return createVirtualDisplays(count);
338             }
339         }
340 
resizeDisplay()341         void resizeDisplay() {
342             executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
343                     + " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY);
344         }
345 
346         @Override
close()347         public void close() throws Exception {
348             mOverlayDisplayDeviceSession.close();
349             if (mVirtualDisplayCreated) {
350                 destroyVirtualDisplays();
351                 mVirtualDisplayCreated = false;
352             }
353         }
354 
355         /**
356          * Simulate new display.
357          * <pre>
358          * <code>mDensityDpi</code> provide custom density for the display.
359          * </pre>
360          * @return {@link ActivityDisplay} of newly created display.
361          */
simulateDisplay()362         private List<ActivityDisplay> simulateDisplay() throws Exception {
363             final List<ActivityDisplay> originalDs = getDisplaysStates();
364 
365             // Create virtual display with custom density dpi and specified size.
366             mOverlayDisplayDeviceSession.set(mSimulationDisplaySize + "/" + mDensityDpi);
367             final List<ActivityDisplay> newDisplays = assertAndGetNewDisplays(1, originalDs);
368 
369             if (mShowSystemDecorations) {
370                 for (ActivityDisplay display : newDisplays) {
371                     mOverlayDisplayDeviceSession.addAndConfigDisplayState(display,
372                             true /* requestShowSysDecors */, true /* requestShowIme */);
373                 }
374             }
375             return newDisplays;
376         }
377 
378         /**
379          * Create new virtual display.
380          * <pre>
381          * <code>mDensityDpi</code> provide custom density for the display.
382          * <code>mLaunchInSplitScreen</code> start
383          *     {@link android.server.wm.app.VirtualDisplayActivity} to side from
384          *     {@link android.server.wm.app.LaunchingActivity} on primary display.
385          * <code>mCanShowWithInsecureKeyguard</code>  allow showing content when device is
386          *     showing an insecure keyguard.
387          * <code>mMustBeCreated</code> should assert if the display was or wasn't created.
388          * <code>mPublicDisplay</code> make display public.
389          * <code>mResizeDisplay</code> should resize display when surface size changes.
390          * <code>LaunchActivity</code> should launch test activity immediately after display
391          *     creation.
392          * </pre>
393          * @param displayCount number of displays to be created.
394          * @return A list of {@link ActivityDisplay} that represent newly created displays.
395          * @throws Exception
396          */
createVirtualDisplays(int displayCount)397         private List<ActivityDisplay> createVirtualDisplays(int displayCount) {
398             // Start an activity that is able to create virtual displays.
399             if (mLaunchInSplitScreen) {
400                 getLaunchActivityBuilder()
401                         .setToSide(true)
402                         .setTargetActivity(VIRTUAL_DISPLAY_ACTIVITY)
403                         .execute();
404             } else {
405                 launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
406             }
407             mAmWmState.computeState(false /* compareTaskAndStackBounds */,
408                     new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
409             mAmWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
410             mAmWmState.assertFocusedActivity("Focus must be on virtual display host activity",
411                     VIRTUAL_DISPLAY_ACTIVITY);
412             final List<ActivityDisplay> originalDS = getDisplaysStates();
413 
414             // Create virtual display with custom density dpi.
415             final StringBuilder createVirtualDisplayCommand = new StringBuilder(
416                     getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
417                     .append(" -f 0x20000000")
418                     .append(" --es " + KEY_COMMAND + " " + COMMAND_CREATE_DISPLAY);
419             if (mDensityDpi != INVALID_DENSITY_DPI) {
420                 createVirtualDisplayCommand
421                         .append(" --ei " + KEY_DENSITY_DPI + " ")
422                         .append(mDensityDpi);
423             }
424             createVirtualDisplayCommand.append(" --ei " + KEY_COUNT + " ").append(displayCount)
425                     .append(" --ez " + KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD + " ")
426                     .append(mCanShowWithInsecureKeyguard)
427                     .append(" --ez " + KEY_PUBLIC_DISPLAY + " ").append(mPublicDisplay)
428                     .append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay)
429                     .append(" --ez " + KEY_SHOW_SYSTEM_DECORATIONS + " ")
430                     .append(mShowSystemDecorations)
431                     .append(" --ez " + KEY_PRESENTATION_DISPLAY + " ").append(mPresentationDisplay);
432             if (mLaunchActivity != null) {
433                 createVirtualDisplayCommand
434                         .append(" --es " + KEY_LAUNCH_TARGET_COMPONENT + " ")
435                         .append(getActivityName(mLaunchActivity));
436             }
437             executeShellCommand(createVirtualDisplayCommand.toString());
438             mVirtualDisplayCreated = true;
439 
440             return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS);
441         }
442 
443         /**
444          * Destroy existing virtual display.
445          */
destroyVirtualDisplays()446         void destroyVirtualDisplays() {
447             final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
448                     + " -f 0x20000000"
449                     + " --es " + KEY_COMMAND + " " + COMMAND_DESTROY_DISPLAY;
450             executeShellCommand(destroyVirtualDisplayCommand);
451             waitForDisplaysDestroyed();
452         }
453 
waitForDisplaysDestroyed()454         private void waitForDisplaysDestroyed() {
455             for (int retry = 1; retry <= 5; retry++) {
456                 if (!isHostedVirtualDisplayPresent()) {
457                     return;
458                 }
459                 logAlways("Waiting for hosted displays destruction... retry=" + retry);
460                 SystemClock.sleep(500);
461             }
462             fail("Waiting for hosted displays destruction failed.");
463         }
464 
isHostedVirtualDisplayPresent()465         private boolean isHostedVirtualDisplayPresent() {
466             mAmWmState.computeState(true);
467             return mAmWmState.getWmState().getDisplays().stream().anyMatch(
468                     d -> d.getName() != null && d.getName().contains(VIRTUAL_DISPLAY_PREFIX));
469         }
470 
471         /**
472          * Wait for desired number of displays to be created and get their properties.
473          * @param newDisplayCount expected display count, -1 if display should not be created.
474          * @param originalDS display states before creation of new display(s).
475          * @return list of new displays, empty list if no new display is created.
476          */
assertAndGetNewDisplays(int newDisplayCount, List<ActivityDisplay> originalDS)477         private List<ActivityDisplay> assertAndGetNewDisplays(int newDisplayCount,
478                 List<ActivityDisplay> originalDS) {
479             final int originalDisplayCount = originalDS.size();
480 
481             // Wait for the display(s) to be created and get configurations.
482             final List<ActivityDisplay> ds = getDisplayStateAfterChange(
483                     originalDisplayCount + newDisplayCount);
484             if (newDisplayCount != -1) {
485                 assertEquals("New virtual display(s) must be created",
486                         originalDisplayCount + newDisplayCount, ds.size());
487             } else {
488                 assertEquals("New virtual display must not be created",
489                         originalDisplayCount, ds.size());
490                 return Collections.emptyList();
491             }
492 
493             // Find the newly added display(s).
494             final List<ActivityDisplay> newDisplays = findNewDisplayStates(originalDS, ds);
495             assertThat("New virtual display must be created",
496                     newDisplays, hasSize(newDisplayCount));
497 
498             return newDisplays;
499         }
500     }
501 
502     // TODO(b/112837428): Merge into VirtualDisplaySession when all usages are migrated.
503     protected class VirtualDisplayLauncher extends VirtualDisplaySession {
504         private final ActivitySessionClient mActivitySessionClient = new ActivitySessionClient();
505 
launchActivityOnDisplay(ComponentName activityName, ActivityDisplay display)506         ActivitySession launchActivityOnDisplay(ComponentName activityName,
507                 ActivityDisplay display) {
508             return launchActivityOnDisplay(activityName, display, null /* extrasConsumer */,
509                     true /* withShellPermission */, true /* waitForLaunch */);
510         }
511 
launchActivityOnDisplay(ComponentName activityName, ActivityDisplay display, Consumer<Bundle> extrasConsumer, boolean withShellPermission, boolean waitForLaunch)512         ActivitySession launchActivityOnDisplay(ComponentName activityName,
513                 ActivityDisplay display, Consumer<Bundle> extrasConsumer,
514                 boolean withShellPermission, boolean waitForLaunch) {
515             return launchActivity(builder -> builder
516                     // VirtualDisplayActivity is in different package. If the display is not public,
517                     // it requires shell permission to launch activity ({@see com.android.server.wm.
518                     // ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplay}).
519                     .setWithShellPermission(withShellPermission)
520                     .setWaitForLaunched(waitForLaunch)
521                     .setIntentExtra(extrasConsumer)
522                     .setTargetActivity(activityName)
523                     .setDisplayId(display.mId));
524         }
525 
launchActivity(Consumer<LaunchActivityBuilder> setupBuilder)526         ActivitySession launchActivity(Consumer<LaunchActivityBuilder> setupBuilder) {
527             final LaunchActivityBuilder builder = getLaunchActivityBuilder()
528                     .setUseInstrumentation();
529             setupBuilder.accept(builder);
530             return mActivitySessionClient.startActivity(builder);
531         }
532 
533         @Override
close()534         public void close() throws Exception {
535             super.close();
536             mActivitySessionClient.close();
537         }
538     }
539 
540     /** Helper class to save, set, and restore overlay_display_devices preference. */
541     private static class OverlayDisplayDevicesSession extends SettingsSession<String> {
542         private final List<OverlayDisplayState> mDisplayStates = new ArrayList<>();
543         private final WindowManager mWm;
544 
OverlayDisplayDevicesSession(Context context)545         OverlayDisplayDevicesSession(Context context) {
546             super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
547                     Settings.Global::getString,
548                     Settings.Global::putString);
549             mWm = context.getSystemService(WindowManager.class);
550         }
551 
addAndConfigDisplayState(ActivityDisplay display, boolean requestShowSysDecors, boolean requestShowIme)552         void addAndConfigDisplayState(ActivityDisplay display, boolean requestShowSysDecors,
553                 boolean requestShowIme) {
554             SystemUtil.runWithShellPermissionIdentity(() -> {
555                 final boolean showSystemDecors = mWm.shouldShowSystemDecors(display.mId);
556                 final boolean showIme = mWm.shouldShowIme(display.mId);
557                 mDisplayStates.add(new OverlayDisplayState(display.mId, showSystemDecors, showIme));
558                 if (requestShowSysDecors != showSystemDecors) {
559                     mWm.setShouldShowSystemDecors(display.mId, requestShowSysDecors);
560                     TestUtils.waitUntil("Waiting for display show system decors",
561                             5 /* timeoutSecond */,
562                             () -> mWm.shouldShowSystemDecors(display.mId) == requestShowSysDecors);
563                 }
564                 if (requestShowIme != showIme) {
565                     mWm.setShouldShowIme(display.mId, requestShowIme);
566                     TestUtils.waitUntil("Waiting for display show Ime",
567                             5 /* timeoutSecond */,
568                             () -> mWm.shouldShowIme(display.mId) == requestShowIme);
569                 }
570             });
571         }
572 
restoreDisplayStates()573         private void restoreDisplayStates() {
574             mDisplayStates.forEach(state -> SystemUtil.runWithShellPermissionIdentity(() -> {
575                 mWm.setShouldShowSystemDecors(state.mId, state.mShouldShowSystemDecors);
576                 mWm.setShouldShowIme(state.mId, state.mShouldShowIme);
577 
578                 // Only need to wait the last flag to be set.
579                 TestUtils.waitUntil("Waiting for the show IME flag to be set",
580                         5 /* timeoutSecond */,
581                         () -> mWm.shouldShowIme(state.mId) == state.mShouldShowIme);
582             }));
583         }
584 
585         @Override
close()586         public void close() throws Exception {
587             // Need to restore display state before display is destroyed.
588             restoreDisplayStates();
589             super.close();
590         }
591 
592         private class OverlayDisplayState {
593             int mId;
594             boolean mShouldShowSystemDecors;
595             boolean mShouldShowIme;
596 
OverlayDisplayState(int displayId, boolean showSysDecors, boolean showIme)597             OverlayDisplayState(int displayId, boolean showSysDecors, boolean showIme) {
598                 mId = displayId;
599                 mShouldShowSystemDecors = showSysDecors;
600                 mShouldShowIme = showIme;
601             }
602         }
603     }
604 
605     /** Wait for provided number of displays and report their configurations. */
getDisplayStateAfterChange(int expectedDisplayCount)606     List<ActivityDisplay> getDisplayStateAfterChange(int expectedDisplayCount) {
607         List<ActivityDisplay> ds = getDisplaysStates();
608 
609         int retriesLeft = 5;
610         while (!areDisplaysValid(ds, expectedDisplayCount) && retriesLeft-- > 0) {
611             log("***Waiting for the correct number of displays...");
612             try {
613                 Thread.sleep(1000);
614             } catch (InterruptedException e) {
615                 log(e.toString());
616             }
617             ds = getDisplaysStates();
618         }
619 
620         return ds;
621     }
622 
areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount)623     private boolean areDisplaysValid(List<ActivityDisplay> displays, int expectedDisplayCount) {
624         if (displays.size() != expectedDisplayCount) {
625             return false;
626         }
627         for (ActivityDisplay display : displays) {
628             if (display.mOverrideConfiguration.densityDpi == 0) {
629                 return false;
630             }
631         }
632         return true;
633     }
634 
635     /** Checks if the device supports multi-display. */
supportsMultiDisplay()636     protected boolean supportsMultiDisplay() {
637         return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
638     }
639 
640     /**
641      * This class is used when you need to test virtual display created by a privileged app.
642      *
643      * If you need to test virtual display created by a non-privileged app or when you need to test
644      * on simulated display, please use {@link VirtualDisplaySession} instead.
645      */
646     public class ExternalDisplaySession implements AutoCloseable {
647 
648         private boolean mCanShowWithInsecureKeyguard = false;
649         private boolean mPublicDisplay = false;
650         private boolean mShowSystemDecorations = false;
651 
652         private int mDisplayId = INVALID_DISPLAY;
653 
654         @Nullable
655         private VirtualDisplayHelper mExternalDisplayHelper;
656 
setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)657         ExternalDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
658             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
659             return this;
660         }
661 
setPublicDisplay(boolean publicDisplay)662         ExternalDisplaySession setPublicDisplay(boolean publicDisplay) {
663             mPublicDisplay = publicDisplay;
664             return this;
665         }
666 
setShowSystemDecorations(boolean showSystemDecorations)667         ExternalDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
668             mShowSystemDecorations = showSystemDecorations;
669             return this;
670         }
671 
672         /**
673          * Creates a private virtual display with insecure keyguard flags set.
674          */
createVirtualDisplay()675         ActivityDisplay createVirtualDisplay() throws Exception {
676             final List<ActivityDisplay> originalDS = getDisplaysStates();
677             final int originalDisplayCount = originalDS.size();
678 
679             mExternalDisplayHelper = new VirtualDisplayHelper();
680             mExternalDisplayHelper
681                     .setPublicDisplay(mPublicDisplay)
682                     .setCanShowWithInsecureKeyguard(mCanShowWithInsecureKeyguard)
683                     .setShowSystemDecorations(mShowSystemDecorations)
684                     .createAndWaitForDisplay();
685 
686             // Wait for the virtual display to be created and get configurations.
687             final List<ActivityDisplay> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
688             assertEquals("New virtual display must be created", originalDisplayCount + 1,
689                     ds.size());
690 
691             // Find the newly added display.
692             final ActivityDisplay newDisplay = findNewDisplayStates(originalDS, ds).get(0);
693             mDisplayId = newDisplay.mId;
694             return newDisplay;
695         }
696 
turnDisplayOff()697         void turnDisplayOff() {
698             if (mExternalDisplayHelper == null) {
699                 throw new RuntimeException("No external display created");
700             }
701             mExternalDisplayHelper.turnDisplayOff();
702         }
703 
turnDisplayOn()704         void turnDisplayOn() {
705             if (mExternalDisplayHelper == null) {
706                 throw new RuntimeException("No external display created");
707             }
708             mExternalDisplayHelper.turnDisplayOn();
709         }
710 
711         @Override
close()712         public void close() throws Exception {
713             if (mExternalDisplayHelper != null) {
714                 mExternalDisplayHelper.releaseDisplay();
715                 mExternalDisplayHelper = null;
716 
717                 waitForHostedDisplayDestroyed();
718                 mDisplayId = INVALID_DISPLAY;
719             }
720         }
721 
waitForHostedDisplayDestroyed()722         private void waitForHostedDisplayDestroyed() {
723             for (int retry = 1; retry <= 5; retry++) {
724                 if (!isHostedVirtualDisplayPresent()) {
725                     return;
726                 }
727                 logAlways("Waiting for hosted displays destruction... retry=" + retry);
728                 SystemClock.sleep(500);
729             }
730             fail("Waiting for hosted displays destruction failed.");
731         }
732 
isHostedVirtualDisplayPresent()733         private boolean isHostedVirtualDisplayPresent() {
734             mAmWmState.computeState(true);
735             return mAmWmState.getWmState().getDisplays().stream().anyMatch(
736                     d -> d.getDisplayId() == mDisplayId);
737         }
738     }
739 
740     public static class PrimaryDisplayStateSession implements AutoCloseable {
741 
turnScreenOff()742         void turnScreenOff() {
743             setPrimaryDisplayState(false);
744         }
745 
746         @Override
close()747         public void close() throws Exception {
748             setPrimaryDisplayState(true);
749         }
750 
751         /** Turns the primary display on/off by pressing the power key */
setPrimaryDisplayState(boolean wantOn)752         private void setPrimaryDisplayState(boolean wantOn) {
753             if (wantOn) {
754                 pressWakeupButton();
755             } else {
756                 pressSleepButton();
757             }
758             VirtualDisplayHelper.waitForDefaultDisplayState(wantOn);
759         }
760     }
761 }
762