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.ActivityInfo.CONFIG_SCREEN_SIZE;
20 import static android.content.pm.PackageManager.FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS;
21 import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
22 import static android.provider.Settings.Secure.IMMERSIVE_MODE_CONFIRMATIONS;
23 import static android.server.wm.StateLogger.log;
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_PRESENTATION_DISPLAY;
35 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_PUBLIC_DISPLAY;
36 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_RESIZE_DISPLAY;
37 import static android.server.wm.app.Components.VirtualDisplayActivity.KEY_SHOW_SYSTEM_DECORATIONS;
38 import static android.server.wm.app.Components.VirtualDisplayActivity.VIRTUAL_DISPLAY_PREFIX;
39 import static android.view.Display.DEFAULT_DISPLAY;
40 import static android.view.Display.INVALID_DISPLAY;
41 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
42 
43 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
44 
45 import static com.android.cts.mockime.ImeEventStreamTestUtils.clearAllEvents;
46 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
47 import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
48 
49 import static org.hamcrest.MatcherAssert.assertThat;
50 import static org.hamcrest.Matchers.hasSize;
51 import static org.junit.Assert.assertEquals;
52 import static org.junit.Assert.assertNotEquals;
53 import static org.junit.Assert.assertTrue;
54 
55 import android.content.ComponentName;
56 import android.content.Context;
57 import android.content.pm.PackageManager;
58 import android.content.res.Configuration;
59 import android.inputmethodservice.InputMethodService;
60 import android.os.Bundle;
61 import android.provider.Settings;
62 import android.server.wm.CommandSession.ActivitySession;
63 import android.server.wm.CommandSession.ActivitySessionClient;
64 import android.server.wm.WindowManagerState.DisplayContent;
65 import android.server.wm.settings.SettingsSession;
66 import android.util.Pair;
67 import android.util.Size;
68 import android.view.WindowManager;
69 
70 import androidx.annotation.NonNull;
71 import androidx.annotation.Nullable;
72 
73 import com.android.compatibility.common.util.SystemUtil;
74 import com.android.cts.mockime.ImeEvent;
75 import com.android.cts.mockime.ImeEventStream;
76 import com.android.cts.mockime.ImeEventStreamTestUtils;
77 
78 import org.junit.AfterClass;
79 import org.junit.Before;
80 import org.junit.BeforeClass;
81 
82 import java.util.ArrayList;
83 import java.util.Collections;
84 import java.util.List;
85 import java.util.Objects;
86 import java.util.concurrent.TimeUnit;
87 import java.util.function.Consumer;
88 import java.util.function.Predicate;
89 import java.util.regex.Matcher;
90 import java.util.regex.Pattern;
91 
92 /**
93  * Base class for ActivityManager display tests.
94  *
95  * @see DisplayTests
96  * @see MultiDisplayKeyguardTests
97  * @see MultiDisplayLockedKeyguardTests
98  * @see AppConfigurationTests
99  */
100 public class MultiDisplayTestBase extends ActivityManagerTestBase {
101 
102     private static SettingsSession<String> sImmersiveModeConfirmationSetting;
103 
104     static final int CUSTOM_DENSITY_DPI = 222;
105     private static final int INVALID_DENSITY_DPI = -1;
106     protected Context mTargetContext;
107 
108     @BeforeClass
setUpClass()109     public static void setUpClass() {
110         sImmersiveModeConfirmationSetting = new SettingsSession<>(
111                 Settings.Secure.getUriFor(IMMERSIVE_MODE_CONFIRMATIONS),
112                 Settings.Secure::getString, Settings.Secure::putString);
113         sImmersiveModeConfirmationSetting.set("confirmed");
114     }
115 
116     @AfterClass
tearDownClass()117     public static void tearDownClass() {
118         if (sImmersiveModeConfirmationSetting != null) {
119             sImmersiveModeConfirmationSetting.close();
120         }
121     }
122 
123     @Before
124     @Override
setUp()125     public void setUp() throws Exception {
126         super.setUp();
127         mTargetContext = getInstrumentation().getTargetContext();
128     }
129 
getDisplayState(int displayId)130     DisplayContent getDisplayState(int displayId) {
131         return getDisplayState(getDisplaysStates(), displayId);
132     }
133 
getDisplayState(List<DisplayContent> displays, int displayId)134     DisplayContent getDisplayState(List<DisplayContent> displays, int displayId) {
135         for (DisplayContent display : displays) {
136             if (display.mId == displayId) {
137                 return display;
138             }
139         }
140         return null;
141     }
142 
143     /** Return the display state with width, height, dpi. Always not default display. */
getDisplayState(List<DisplayContent> displays, int width, int height, int dpi)144     DisplayContent getDisplayState(List<DisplayContent> displays, int width, int height,
145             int dpi) {
146         for (DisplayContent display : displays) {
147             if (display.mId == DEFAULT_DISPLAY) {
148                 continue;
149             }
150             final Configuration config = display.mFullConfiguration;
151             if (config.densityDpi == dpi && config.screenWidthDp == width
152                     && config.screenHeightDp == height) {
153                 return display;
154             }
155         }
156         return null;
157     }
158 
getDisplaysStates()159     List<DisplayContent> getDisplaysStates() {
160         mWmState.computeState();
161         return mWmState.getDisplays();
162     }
163 
164     /** Find the display that was not originally reported in oldDisplays and added in newDisplays */
findNewDisplayStates(List<DisplayContent> oldDisplays, List<DisplayContent> newDisplays)165     List<DisplayContent> findNewDisplayStates(List<DisplayContent> oldDisplays,
166             List<DisplayContent> newDisplays) {
167         final ArrayList<DisplayContent> result = new ArrayList<>();
168 
169         for (DisplayContent newDisplay : newDisplays) {
170             if (oldDisplays.stream().noneMatch(d -> d.mId == newDisplay.mId)) {
171                 result.add(newDisplay);
172             }
173         }
174 
175         return result;
176     }
177 
178     public static class ReportedDisplayMetrics {
179         private static final String WM_SIZE = "wm size";
180         private static final String WM_DENSITY = "wm density";
181         private static final Pattern PHYSICAL_SIZE =
182                 Pattern.compile("Physical size: (\\d+)x(\\d+)");
183         private static final Pattern OVERRIDE_SIZE =
184                 Pattern.compile("Override size: (\\d+)x(\\d+)");
185         private static final Pattern PHYSICAL_DENSITY =
186                 Pattern.compile("Physical density: (\\d+)");
187         private static final Pattern OVERRIDE_DENSITY =
188                 Pattern.compile("Override density: (\\d+)");
189 
190         /** The size of the physical display. */
191         @NonNull
192         final Size physicalSize;
193         /** The density of the physical display. */
194         final int physicalDensity;
195 
196         /** The pre-existing size override applied to a logical display. */
197         @Nullable
198         final Size overrideSize;
199         /** The pre-existing density override applied to a logical display. */
200         @Nullable
201         final Integer overrideDensity;
202 
203         final int mDisplayId;
204 
205         /** Get physical and override display metrics from WM for specified display. */
getDisplayMetrics(int displayId)206         public static ReportedDisplayMetrics getDisplayMetrics(int displayId) {
207             return new ReportedDisplayMetrics(executeShellCommand(WM_SIZE + " -d " + displayId)
208                             + executeShellCommand(WM_DENSITY + " -d " + displayId), displayId);
209         }
210 
setDisplayMetrics(final Size size, final int density)211         void setDisplayMetrics(final Size size, final int density) {
212             setSize(size);
213             setDensity(density);
214         }
215 
restoreDisplayMetrics()216         void restoreDisplayMetrics() {
217             if (overrideSize != null) {
218                 setSize(overrideSize);
219             } else {
220                 executeShellCommand(WM_SIZE + " reset -d " + mDisplayId);
221             }
222             if (overrideDensity != null) {
223                 setDensity(overrideDensity);
224             } else {
225                 executeShellCommand(WM_DENSITY + " reset -d " + mDisplayId);
226             }
227         }
228 
setSize(final Size size)229         private void setSize(final Size size) {
230             executeShellCommand(
231                     WM_SIZE + " " + size.getWidth() + "x" + size.getHeight() + " -d " + mDisplayId);
232         }
233 
setDensity(final int density)234         private void setDensity(final int density) {
235             executeShellCommand(WM_DENSITY + " " + density + " -d " + mDisplayId);
236         }
237 
238         /** Get display size that WM operates with. */
getSize()239         public Size getSize() {
240             return overrideSize != null ? overrideSize : physicalSize;
241         }
242 
243         /** Get density that WM operates with. */
getDensity()244         int getDensity() {
245             return overrideDensity != null ? overrideDensity : physicalDensity;
246         }
247 
ReportedDisplayMetrics(final String lines, int displayId)248         private ReportedDisplayMetrics(final String lines, int displayId) {
249             mDisplayId = displayId;
250             Matcher matcher = PHYSICAL_SIZE.matcher(lines);
251             assertTrue("Physical display size must be reported", matcher.find());
252             log(matcher.group());
253             physicalSize = new Size(
254                     Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
255 
256             matcher = PHYSICAL_DENSITY.matcher(lines);
257             assertTrue("Physical display density must be reported", matcher.find());
258             log(matcher.group());
259             physicalDensity = Integer.parseInt(matcher.group(1));
260 
261             matcher = OVERRIDE_SIZE.matcher(lines);
262             if (matcher.find()) {
263                 log(matcher.group());
264                 overrideSize = new Size(
265                         Integer.parseInt(matcher.group(1)), Integer.parseInt(matcher.group(2)));
266             } else {
267                 overrideSize = null;
268             }
269 
270             matcher = OVERRIDE_DENSITY.matcher(lines);
271             if (matcher.find()) {
272                 log(matcher.group());
273                 overrideDensity = Integer.parseInt(matcher.group(1));
274             } else {
275                 overrideDensity = null;
276             }
277         }
278     }
279 
280     public static class DisplayMetricsSession implements AutoCloseable {
281         private final ReportedDisplayMetrics mInitialDisplayMetrics;
282         private final int mDisplayId;
283 
DisplayMetricsSession(int displayId)284         DisplayMetricsSession(int displayId) {
285             mDisplayId = displayId;
286             mInitialDisplayMetrics = ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
287         }
288 
getInitialDisplayMetrics()289         ReportedDisplayMetrics getInitialDisplayMetrics() {
290             return mInitialDisplayMetrics;
291         }
292 
getDisplayMetrics()293         ReportedDisplayMetrics getDisplayMetrics() {
294             return ReportedDisplayMetrics.getDisplayMetrics(mDisplayId);
295         }
296 
changeAspectRatio(double aspectRatio, int orientation)297         void changeAspectRatio(double aspectRatio, int orientation) {
298             final Size originalSize = mInitialDisplayMetrics.physicalSize;
299             final int smaller = Math.min(originalSize.getWidth(), originalSize.getHeight());
300             final int larger = (int) (smaller * aspectRatio);
301             Size overrideSize;
302             if (orientation == ORIENTATION_LANDSCAPE) {
303                 overrideSize = new Size(larger, smaller);
304             }
305             else {
306                 overrideSize = new Size(smaller, larger);
307             }
308             overrideDisplayMetrics(overrideSize, mInitialDisplayMetrics.physicalDensity);
309         }
310 
changeDisplayMetrics(double sizeRatio, double densityRatio)311         void changeDisplayMetrics(double sizeRatio, double densityRatio) {
312             // Given a display may already have an override applied before the test is begun,
313             // resize based upon the override.
314             final Size originalSize;
315             final int density;
316             if (mInitialDisplayMetrics.overrideSize != null) {
317                 originalSize = mInitialDisplayMetrics.overrideSize;
318             } else {
319                 originalSize = mInitialDisplayMetrics.physicalSize;
320             }
321 
322             if (mInitialDisplayMetrics.overrideDensity != null) {
323                 density = mInitialDisplayMetrics.overrideDensity;
324             } else {
325                 density = mInitialDisplayMetrics.physicalDensity;
326             }
327 
328             final Size overrideSize = new Size((int)(originalSize.getWidth() * sizeRatio),
329                     (int)(originalSize.getHeight() * sizeRatio));
330             final int overrideDensity = (int)(density * densityRatio);
331             overrideDisplayMetrics(overrideSize, overrideDensity);
332         }
333 
overrideDisplayMetrics(final Size size, final int density)334         void overrideDisplayMetrics(final Size size, final int density) {
335             mInitialDisplayMetrics.setDisplayMetrics(size, density);
336         }
337 
restoreDisplayMetrics()338         void restoreDisplayMetrics() {
339             mInitialDisplayMetrics.restoreDisplayMetrics();
340         }
341 
342         @Override
close()343         public void close() {
344             restoreDisplayMetrics();
345         }
346     }
347 
348     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedDisplayMetricsSession(int displayId)349     protected DisplayMetricsSession createManagedDisplayMetricsSession(int displayId) {
350         return mObjectTracker.manage(new DisplayMetricsSession(displayId));
351     }
352 
353     public static class IgnoreOrientationRequestSession implements AutoCloseable {
354         private static final String WM_SET_IGNORE_ORIENTATION_REQUEST =
355                 "wm set-ignore-orientation-request ";
356         private static final String WM_GET_IGNORE_ORIENTATION_REQUEST =
357                 "wm get-ignore-orientation-request";
358         private static final Pattern IGNORE_ORIENTATION_REQUEST_PATTERN =
359                 Pattern.compile("ignoreOrientationRequest (true|false) for displayId=\\d+");
360 
361         final int mDisplayId;
362         final boolean mInitialValue;
363 
IgnoreOrientationRequestSession(int displayId, boolean value)364         IgnoreOrientationRequestSession(int displayId, boolean value) {
365             mDisplayId = displayId;
366             Matcher matcher = IGNORE_ORIENTATION_REQUEST_PATTERN.matcher(
367                     executeShellCommand(WM_GET_IGNORE_ORIENTATION_REQUEST + " -d " + mDisplayId));
368             assertTrue("get-ignore-orientation-request should match pattern", matcher.find());
369             mInitialValue = Boolean.parseBoolean(matcher.group(1));
370 
371             executeShellCommand("wm set-ignore-orientation-request true -d " + mDisplayId);
372             executeShellCommand(WM_SET_IGNORE_ORIENTATION_REQUEST + value + " -d " + mDisplayId);
373         }
374 
375         @Override
close()376         public void close() {
377             executeShellCommand(
378                     WM_SET_IGNORE_ORIENTATION_REQUEST + mInitialValue + " -d " + mDisplayId);
379         }
380     }
381 
382     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedIgnoreOrientationRequestSession( int displayId, boolean value)383     protected IgnoreOrientationRequestSession createManagedIgnoreOrientationRequestSession(
384             int displayId, boolean value) {
385         return mObjectTracker.manage(new IgnoreOrientationRequestSession(displayId, value));
386     }
387 
waitForDisplayGone(Predicate<DisplayContent> displayPredicate)388     void waitForDisplayGone(Predicate<DisplayContent> displayPredicate) {
389         waitForOrFail("displays to be removed", () -> {
390             mWmState.computeState();
391             return mWmState.getDisplays().stream().noneMatch(displayPredicate);
392         });
393     }
394 
395     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedVirtualDisplaySession()396     protected VirtualDisplaySession createManagedVirtualDisplaySession() {
397         return mObjectTracker.manage(new VirtualDisplaySession());
398     }
399 
400     /**
401      * This class should only be used when you need to test virtual display created by a
402      * non-privileged app.
403      * Or when you need to test on simulated display.
404      *
405      * If you need to test virtual display created by a privileged app, please use
406      * {@link ExternalDisplaySession} instead.
407      */
408     public class VirtualDisplaySession implements AutoCloseable {
409         private int mDensityDpi = CUSTOM_DENSITY_DPI;
410         private boolean mLaunchInSplitScreen = false;
411         private boolean mCanShowWithInsecureKeyguard = false;
412         private boolean mPublicDisplay = false;
413         private boolean mResizeDisplay = true;
414         private boolean mShowSystemDecorations = false;
415         private boolean mOwnContentOnly = false;
416         private int mDisplayImePolicy = DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
417         private boolean mPresentationDisplay = false;
418         private boolean mSimulateDisplay = false;
419         private boolean mMustBeCreated = true;
420         private Size mSimulationDisplaySize = new Size(1024 /* width */, 768 /* height */);
421 
422         private boolean mVirtualDisplayCreated = false;
423         private OverlayDisplayDevicesSession mOverlayDisplayDeviceSession;
424 
setDensityDpi(int densityDpi)425         VirtualDisplaySession setDensityDpi(int densityDpi) {
426             mDensityDpi = densityDpi;
427             return this;
428         }
429 
setLaunchInSplitScreen(boolean launchInSplitScreen)430         VirtualDisplaySession setLaunchInSplitScreen(boolean launchInSplitScreen) {
431             mLaunchInSplitScreen = launchInSplitScreen;
432             return this;
433         }
434 
setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)435         VirtualDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
436             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
437             return this;
438         }
439 
setPublicDisplay(boolean publicDisplay)440         VirtualDisplaySession setPublicDisplay(boolean publicDisplay) {
441             mPublicDisplay = publicDisplay;
442             return this;
443         }
444 
setResizeDisplay(boolean resizeDisplay)445         VirtualDisplaySession setResizeDisplay(boolean resizeDisplay) {
446             mResizeDisplay = resizeDisplay;
447             return this;
448         }
449 
setShowSystemDecorations(boolean showSystemDecorations)450         VirtualDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
451             mShowSystemDecorations = showSystemDecorations;
452             return this;
453         }
454 
setOwnContentOnly(boolean ownContentOnly)455         VirtualDisplaySession setOwnContentOnly(boolean ownContentOnly) {
456             mOwnContentOnly = ownContentOnly;
457             return this;
458         }
459 
460         /**
461          * Sets the policy for how the display should show the ime.
462          *
463          * Set to one of:
464          *   <ul>
465          *     <li>{@link WindowManager#DISPLAY_IME_POLICY_LOCAL}
466          *     <li>{@link WindowManager#DISPLAY_IME_POLICY_FALLBACK_DISPLAY}
467          *     <li>{@link WindowManager#DISPLAY_IME_POLICY_HIDE}
468          *   </ul>
469          */
setDisplayImePolicy(int displayImePolicy)470         VirtualDisplaySession setDisplayImePolicy(int displayImePolicy) {
471             mDisplayImePolicy = displayImePolicy;
472             return this;
473         }
474 
setPresentationDisplay(boolean presentationDisplay)475         VirtualDisplaySession setPresentationDisplay(boolean presentationDisplay) {
476             mPresentationDisplay = presentationDisplay;
477             return this;
478         }
479 
480         // TODO(b/154565343) move simulate display out of VirtualDisplaySession
setSimulateDisplay(boolean simulateDisplay)481         public VirtualDisplaySession setSimulateDisplay(boolean simulateDisplay) {
482             mSimulateDisplay = simulateDisplay;
483             return this;
484         }
485 
setSimulationDisplaySize(int width, int height)486         VirtualDisplaySession setSimulationDisplaySize(int width, int height) {
487             mSimulationDisplaySize = new Size(width, height);
488             return this;
489         }
490 
491         @Nullable
createDisplay(boolean mustBeCreated)492         public DisplayContent createDisplay(boolean mustBeCreated) {
493             mMustBeCreated = mustBeCreated;
494             final DisplayContent display = createDisplays(1).stream().findFirst().orElse(null);
495             if (mustBeCreated && display == null) {
496                 throw new IllegalStateException("No display is created");
497             }
498             return display;
499         }
500 
501         @NonNull
createDisplay()502         public DisplayContent createDisplay() {
503             return Objects.requireNonNull(createDisplay(true /* mustBeCreated */));
504         }
505 
506         @NonNull
createDisplays(int count)507         List<DisplayContent> createDisplays(int count) {
508             if (mSimulateDisplay) {
509                 return simulateDisplays(count);
510             } else {
511                 return createVirtualDisplays(count);
512             }
513         }
514 
resizeDisplay()515         void resizeDisplay() {
516             if (mSimulateDisplay) {
517                 throw new IllegalStateException(
518                         "Please use ReportedDisplayMetrics#setDisplayMetrics to resize"
519                                 + " simulate display");
520             }
521             executeShellCommand(getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
522                     + " -f 0x20000000" + " --es " + KEY_COMMAND + " " + COMMAND_RESIZE_DISPLAY);
523         }
524 
525         @Override
close()526         public void close()  {
527             if (mOverlayDisplayDeviceSession != null) {
528                 mOverlayDisplayDeviceSession.close();
529             }
530             if (mVirtualDisplayCreated) {
531                 destroyVirtualDisplays();
532                 mVirtualDisplayCreated = false;
533             }
534         }
535 
536         /**
537          * Simulate new display.
538          * <pre>
539          * <code>mDensityDpi</code> provide custom density for the display.
540          * </pre>
541          * @return {@link DisplayContent} of newly created display.
542          */
simulateDisplays(int count)543         private List<DisplayContent> simulateDisplays(int count) {
544             mOverlayDisplayDeviceSession = new OverlayDisplayDevicesSession(mContext);
545             mOverlayDisplayDeviceSession.createDisplays(mSimulationDisplaySize, mDensityDpi,
546                     mOwnContentOnly, mShowSystemDecorations, count);
547             mOverlayDisplayDeviceSession.configureDisplays(mDisplayImePolicy /* imePolicy */);
548             return mOverlayDisplayDeviceSession.getCreatedDisplays();
549         }
550 
551         /**
552          * Create new virtual display.
553          * <pre>
554          * <code>mDensityDpi</code> provide custom density for the display.
555          * <code>mLaunchInSplitScreen</code> start
556          *     {@link android.server.wm.app.VirtualDisplayActivity} to side from
557          *     {@link android.server.wm.app.LaunchingActivity} on primary display.
558          * <code>mCanShowWithInsecureKeyguard</code>  allow showing content when device is
559          *     showing an insecure keyguard.
560          * <code>mMustBeCreated</code> should assert if the display was or wasn't created.
561          * <code>mPublicDisplay</code> make display public.
562          * <code>mResizeDisplay</code> should resize display when surface size changes.
563          * <code>LaunchActivity</code> should launch test activity immediately after display
564          *     creation.
565          * </pre>
566          * @param displayCount number of displays to be created.
567          * @return A list of {@link DisplayContent} that represent newly created displays.
568          * @throws Exception
569          */
createVirtualDisplays(int displayCount)570         private List<DisplayContent> createVirtualDisplays(int displayCount) {
571             // Start an activity that is able to create virtual displays.
572             if (mLaunchInSplitScreen) {
573                 getLaunchActivityBuilder()
574                         .setToSide(true)
575                         .setTargetActivity(VIRTUAL_DISPLAY_ACTIVITY)
576                         .execute();
577                 final int secondaryTaskId =
578                         mWmState.getTaskByActivity(VIRTUAL_DISPLAY_ACTIVITY).mTaskId;
579                 mTaskOrganizer.putTaskInSplitSecondary(secondaryTaskId);
580             } else {
581                 launchActivity(VIRTUAL_DISPLAY_ACTIVITY);
582             }
583             mWmState.computeState(
584                     new WaitForValidActivityState(VIRTUAL_DISPLAY_ACTIVITY));
585             mWmState.assertVisibility(VIRTUAL_DISPLAY_ACTIVITY, true /* visible */);
586             mWmState.assertFocusedActivity("Focus must be on virtual display host activity",
587                     VIRTUAL_DISPLAY_ACTIVITY);
588             final List<DisplayContent> originalDS = getDisplaysStates();
589 
590             // Create virtual display with custom density dpi.
591             final StringBuilder createVirtualDisplayCommand = new StringBuilder(
592                     getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY))
593                     .append(" -f 0x20000000")
594                     .append(" --es " + KEY_COMMAND + " " + COMMAND_CREATE_DISPLAY);
595             if (mDensityDpi != INVALID_DENSITY_DPI) {
596                 createVirtualDisplayCommand
597                         .append(" --ei " + KEY_DENSITY_DPI + " ")
598                         .append(mDensityDpi);
599             }
600             createVirtualDisplayCommand.append(" --ei " + KEY_COUNT + " ").append(displayCount)
601                     .append(" --ez " + KEY_CAN_SHOW_WITH_INSECURE_KEYGUARD + " ")
602                     .append(mCanShowWithInsecureKeyguard)
603                     .append(" --ez " + KEY_PUBLIC_DISPLAY + " ").append(mPublicDisplay)
604                     .append(" --ez " + KEY_RESIZE_DISPLAY + " ").append(mResizeDisplay)
605                     .append(" --ez " + KEY_SHOW_SYSTEM_DECORATIONS + " ")
606                     .append(mShowSystemDecorations)
607                     .append(" --ez " + KEY_PRESENTATION_DISPLAY + " ").append(mPresentationDisplay);
608             executeShellCommand(createVirtualDisplayCommand.toString());
609             mVirtualDisplayCreated = true;
610 
611             return assertAndGetNewDisplays(mMustBeCreated ? displayCount : -1, originalDS);
612         }
613 
614         /**
615          * Destroy existing virtual display.
616          */
destroyVirtualDisplays()617         void destroyVirtualDisplays() {
618             final String destroyVirtualDisplayCommand = getAmStartCmd(VIRTUAL_DISPLAY_ACTIVITY)
619                     + " -f 0x20000000"
620                     + " --es " + KEY_COMMAND + " " + COMMAND_DESTROY_DISPLAY;
621             executeShellCommand(destroyVirtualDisplayCommand);
622             waitForDisplayGone(
623                     d -> d.getName() != null && d.getName().contains(VIRTUAL_DISPLAY_PREFIX));
624         }
625     }
626 
627     // TODO(b/112837428): Merge into VirtualDisplaySession when all usages are migrated.
628     protected class VirtualDisplayLauncher extends VirtualDisplaySession {
629         private final ActivitySessionClient mActivitySessionClient = createActivitySessionClient();
630 
launchActivityOnDisplay(ComponentName activityName, DisplayContent display)631         ActivitySession launchActivityOnDisplay(ComponentName activityName,
632                 DisplayContent display) {
633             return launchActivityOnDisplay(activityName, display, null /* extrasConsumer */,
634                     true /* withShellPermission */, true /* waitForLaunch */);
635         }
636 
launchActivityOnDisplay(ComponentName activityName, DisplayContent display, Consumer<Bundle> extrasConsumer, boolean withShellPermission, boolean waitForLaunch)637         ActivitySession launchActivityOnDisplay(ComponentName activityName,
638                 DisplayContent display, Consumer<Bundle> extrasConsumer,
639                 boolean withShellPermission, boolean waitForLaunch) {
640             return launchActivity(builder -> builder
641                     // VirtualDisplayActivity is in different package. If the display is not public,
642                     // it requires shell permission to launch activity ({@see com.android.server.wm.
643                     // ActivityStackSupervisor#isCallerAllowedToLaunchOnDisplay}).
644                     .setWithShellPermission(withShellPermission)
645                     .setWaitForLaunched(waitForLaunch)
646                     .setIntentExtra(extrasConsumer)
647                     .setTargetActivity(activityName)
648                     .setDisplayId(display.mId));
649         }
650 
launchActivity(Consumer<LaunchActivityBuilder> setupBuilder)651         ActivitySession launchActivity(Consumer<LaunchActivityBuilder> setupBuilder) {
652             final LaunchActivityBuilder builder = getLaunchActivityBuilder()
653                     .setUseInstrumentation();
654             setupBuilder.accept(builder);
655             return mActivitySessionClient.startActivity(builder);
656         }
657 
658         @Override
close()659         public void close() {
660             super.close();
661             mActivitySessionClient.close();
662         }
663     }
664 
665     /** Helper class to save, set, and restore overlay_display_devices preference. */
666     private class OverlayDisplayDevicesSession extends SettingsSession<String> {
667         /** See display_manager_overlay_display_name. */
668         private static final String OVERLAY_DISPLAY_NAME_PREFIX = "Overlay #";
669 
670         /** See {@link OverlayDisplayAdapter#OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY}. */
671         private static final String OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY = ",own_content_only";
672 
673         /**
674          * See {@link OverlayDisplayAdapter#OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS}.
675          */
676         private static final String OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS =
677                 ",should_show_system_decorations";
678 
679         /** The displays which are created by this session. */
680         private final List<DisplayContent> mDisplays = new ArrayList<>();
681         /** The configured displays that need to be restored when this session is closed. */
682         private final List<OverlayDisplayState> mDisplayStates = new ArrayList<>();
683         private final WindowManager mWm;
684 
OverlayDisplayDevicesSession(Context context)685         OverlayDisplayDevicesSession(Context context) {
686             super(Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
687                     Settings.Global::getString,
688                     Settings.Global::putString);
689             // Remove existing overlay display to avoid display count problem.
690             removeExisting();
691             mWm = context.getSystemService(WindowManager.class);
692         }
693 
getCreatedDisplays()694         List<DisplayContent> getCreatedDisplays() {
695             return new ArrayList<>(mDisplays);
696         }
697 
698         @Override
set(String value)699         public void set(String value) {
700             final List<DisplayContent> originalDisplays = getDisplaysStates();
701             super.set(value);
702             final int newDisplayCount = 1 + (int) value.chars().filter(ch -> ch == ';').count();
703             mDisplays.addAll(assertAndGetNewDisplays(newDisplayCount, originalDisplays));
704         }
705 
706         /** Creates overlay display with custom density dpi, specified size, and test flags. */
createDisplays(Size displaySize, int densityDpi, boolean ownContentOnly, boolean shouldShowSystemDecorations, int count)707         void createDisplays(Size displaySize, int densityDpi, boolean ownContentOnly,
708                 boolean shouldShowSystemDecorations, int count) {
709             final StringBuilder builder = new StringBuilder();
710             for (int i = 0; i < count; i++) {
711                 String displaySettingsEntry = displaySize + "/" + densityDpi;
712                 if (ownContentOnly) {
713                     displaySettingsEntry += OVERLAY_DISPLAY_FLAG_OWN_CONTENT_ONLY;
714                 }
715                 if (shouldShowSystemDecorations) {
716                     displaySettingsEntry += OVERLAY_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
717                 }
718                 builder.append(displaySettingsEntry);
719                 // Creating n displays needs (n - 1) ';'.
720                 if (i < count - 1) {
721                     builder.append(';');
722                 }
723             }
724             set(builder.toString());
725         }
726 
configureDisplays(int imePolicy)727         void configureDisplays(int imePolicy) {
728             SystemUtil.runWithShellPermissionIdentity(() -> {
729                 for (DisplayContent display : mDisplays) {
730                     final int oldImePolicy = mWm.getDisplayImePolicy(display.mId);
731                     mDisplayStates.add(new OverlayDisplayState(display.mId, oldImePolicy));
732                     if (imePolicy != oldImePolicy) {
733                         mWm.setDisplayImePolicy(display.mId, imePolicy);
734                         waitForOrFail("display config show-IME to be set",
735                                 () -> (mWm.getDisplayImePolicy(display.mId) == imePolicy));
736                     }
737                 }
738             });
739         }
740 
restoreDisplayStates()741         private void restoreDisplayStates() {
742             mDisplayStates.forEach(state -> SystemUtil.runWithShellPermissionIdentity(() -> {
743                 mWm.setDisplayImePolicy(state.mId, state.mImePolicy);
744 
745                 // Only need to wait the last flag to be set.
746                 waitForOrFail("display config show-IME to be restored",
747                         () -> (mWm.getDisplayImePolicy(state.mId) == state.mImePolicy));
748             }));
749         }
750 
751         @Override
close()752         public void close() {
753             // Need to restore display state before display is destroyed.
754             restoreDisplayStates();
755             super.close();
756             // Waiting for restoring to the state before this session was created.
757             waitForDisplayGone(display -> mDisplays.stream()
758                     .anyMatch(createdDisplay -> createdDisplay.mId == display.mId));
759         }
760 
removeExisting()761         private void removeExisting() {
762             if (!mHasInitialValue || mInitialValue == null) {
763                 // No existing overlay displays.
764                 return;
765             }
766             delete(mUri);
767             // Make sure all overlay displays are completely removed.
768             waitForDisplayGone(
769                     display -> display.getName().startsWith(OVERLAY_DISPLAY_NAME_PREFIX));
770         }
771 
772         private class OverlayDisplayState {
773             int mId;
774             int mImePolicy;
775 
OverlayDisplayState(int displayId, int imePolicy)776             OverlayDisplayState(int displayId, int imePolicy) {
777                 mId = displayId;
778                 mImePolicy = imePolicy;
779             }
780         }
781     }
782 
783     /** Wait for provided number of displays and report their configurations. */
getDisplayStateAfterChange(int expectedDisplayCount)784     List<DisplayContent> getDisplayStateAfterChange(int expectedDisplayCount) {
785         return Condition.waitForResult("the correct number of displays=" + expectedDisplayCount,
786                 condition -> condition
787                         .setReturnLastResult(true)
788                         .setResultSupplier(this::getDisplaysStates)
789                         .setResultValidator(
790                                 displays -> areDisplaysValid(displays, expectedDisplayCount)));
791     }
792 
areDisplaysValid(List<DisplayContent> displays, int expectedDisplayCount)793     private boolean areDisplaysValid(List<DisplayContent> displays, int expectedDisplayCount) {
794         if (displays.size() != expectedDisplayCount) {
795             return false;
796         }
797         for (DisplayContent display : displays) {
798             if (display.mOverrideConfiguration.densityDpi == 0) {
799                 return false;
800             }
801         }
802         return true;
803     }
804 
805     /**
806      * Wait for desired number of displays to be created and get their properties.
807      *
808      * @param newDisplayCount expected display count, -1 if display should not be created.
809      * @param originalDisplays display states before creation of new display(s).
810      * @return list of new displays, empty list if no new display is created.
811      */
assertAndGetNewDisplays(int newDisplayCount, List<DisplayContent> originalDisplays)812     private List<DisplayContent> assertAndGetNewDisplays(int newDisplayCount,
813             List<DisplayContent> originalDisplays) {
814         final int originalDisplayCount = originalDisplays.size();
815 
816         // Wait for the display(s) to be created and get configurations.
817         final List<DisplayContent> ds = getDisplayStateAfterChange(
818                 originalDisplayCount + newDisplayCount);
819         if (newDisplayCount != -1) {
820             assertEquals("New virtual display(s) must be created",
821                     originalDisplayCount + newDisplayCount, ds.size());
822         } else {
823             assertEquals("New virtual display must not be created",
824                     originalDisplayCount, ds.size());
825             return Collections.emptyList();
826         }
827 
828         // Find the newly added display(s).
829         final List<DisplayContent> newDisplays = findNewDisplayStates(originalDisplays, ds);
830         assertThat("New virtual display must be created", newDisplays, hasSize(newDisplayCount));
831 
832         return newDisplays;
833     }
834 
835     /** A clearer alias of {@link Pair#create(Object, Object)}. */
pair(K k, V v)836     protected <K, V> Pair<K, V> pair(K k, V v) {
837         return new Pair<>(k, v);
838     }
839 
assertBothDisplaysHaveResumedActivities( Pair<Integer, ComponentName> firstPair, Pair<Integer, ComponentName> secondPair)840     protected void assertBothDisplaysHaveResumedActivities(
841             Pair<Integer, ComponentName> firstPair, Pair<Integer, ComponentName> secondPair) {
842         assertNotEquals("Displays must be different.  First display id: "
843                         + firstPair.first, firstPair.first, secondPair.first);
844         mWmState.assertResumedActivities("Both displays must have resumed activities",
845                 mapping -> {
846                     mapping.put(firstPair.first, firstPair.second);
847                     mapping.put(secondPair.first, secondPair.second);
848                 });
849     }
850 
851     /** Checks if the device supports multi-display. */
supportsMultiDisplay()852     protected boolean supportsMultiDisplay() {
853         return hasDeviceFeature(FEATURE_ACTIVITIES_ON_SECONDARY_DISPLAYS);
854     }
855 
856     /** Checks if the device supports wallpaper for multi-display. */
supportsLiveWallpaper()857     protected boolean supportsLiveWallpaper() {
858         return hasDeviceFeature(PackageManager.FEATURE_LIVE_WALLPAPER);
859     }
860 
861     /** @see ObjectTracker#manage(AutoCloseable) */
createManagedExternalDisplaySession()862     protected ExternalDisplaySession createManagedExternalDisplaySession() {
863         return mObjectTracker.manage(new ExternalDisplaySession());
864     }
865 
866     @SafeVarargs
waitOrderedImeEventsThenAssertImeShown(ImeEventStream stream, int displayId, Predicate<ImeEvent>... conditions)867     final void waitOrderedImeEventsThenAssertImeShown(ImeEventStream stream, int displayId,
868             Predicate<ImeEvent>... conditions) throws Exception {
869         for (Predicate<ImeEvent> condition : conditions) {
870             expectEvent(stream, condition, TimeUnit.SECONDS.toMillis(5) /* eventTimeout */);
871         }
872         // Assert the IME is shown on the expected display.
873         mWmState.waitAndAssertImeWindowShownOnDisplay(displayId);
874     }
875 
waitAndAssertImeNoScreenSizeChanged(ImeEventStream stream)876     protected void waitAndAssertImeNoScreenSizeChanged(ImeEventStream stream) {
877         notExpectEvent(stream, event -> "onConfigurationChanged".equals(event.getEventName())
878                 && (event.getArguments().getInt("ConfigUpdates") & CONFIG_SCREEN_SIZE) != 0,
879                 TimeUnit.SECONDS.toMillis(1) /* eventTimeout */);
880     }
881 
882     /**
883      * Clears all {@link InputMethodService#onConfigurationChanged(Configuration)} events from the
884      * given {@code stream} and returns a forked {@link ImeEventStream}.
885      *
886      * @see ImeEventStreamTestUtils#clearAllEvents(ImeEventStream, String)
887      */
clearOnConfigurationChangedFromStream(ImeEventStream stream)888     protected ImeEventStream clearOnConfigurationChangedFromStream(ImeEventStream stream) {
889         return clearAllEvents(stream, "onConfigurationChanged");
890     }
891 
892     /**
893      * This class is used when you need to test virtual display created by a privileged app.
894      *
895      * If you need to test virtual display created by a non-privileged app or when you need to test
896      * on simulated display, please use {@link VirtualDisplaySession} instead.
897      */
898     public class ExternalDisplaySession implements AutoCloseable {
899 
900         private boolean mCanShowWithInsecureKeyguard = false;
901         private boolean mPublicDisplay = false;
902         private boolean mShowSystemDecorations = false;
903 
904         private int mDisplayId = INVALID_DISPLAY;
905 
906         @Nullable
907         private VirtualDisplayHelper mExternalDisplayHelper;
908 
setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard)909         ExternalDisplaySession setCanShowWithInsecureKeyguard(boolean canShowWithInsecureKeyguard) {
910             mCanShowWithInsecureKeyguard = canShowWithInsecureKeyguard;
911             return this;
912         }
913 
setPublicDisplay(boolean publicDisplay)914         ExternalDisplaySession setPublicDisplay(boolean publicDisplay) {
915             mPublicDisplay = publicDisplay;
916             return this;
917         }
918 
919         /**
920          * @deprecated untrusted virtual display won't have system decorations even it has the flag.
921          * Only use this method to verify that. To test secondary display with system decorations,
922          * please use simulated display.
923          */
924         @Deprecated
setShowSystemDecorations(boolean showSystemDecorations)925         ExternalDisplaySession setShowSystemDecorations(boolean showSystemDecorations) {
926             mShowSystemDecorations = showSystemDecorations;
927             return this;
928         }
929 
930         /**
931          * Creates a private virtual display with insecure keyguard flags set.
932          */
createVirtualDisplay()933         DisplayContent createVirtualDisplay() {
934             final List<DisplayContent> originalDS = getDisplaysStates();
935             final int originalDisplayCount = originalDS.size();
936 
937             mExternalDisplayHelper = new VirtualDisplayHelper();
938             mExternalDisplayHelper
939                     .setPublicDisplay(mPublicDisplay)
940                     .setCanShowWithInsecureKeyguard(mCanShowWithInsecureKeyguard)
941                     .setShowSystemDecorations(mShowSystemDecorations)
942                     .createAndWaitForDisplay();
943 
944             // Wait for the virtual display to be created and get configurations.
945             final List<DisplayContent> ds = getDisplayStateAfterChange(originalDisplayCount + 1);
946             assertEquals("New virtual display must be created", originalDisplayCount + 1,
947                     ds.size());
948 
949             // Find the newly added display.
950             final DisplayContent newDisplay = findNewDisplayStates(originalDS, ds).get(0);
951             mDisplayId = newDisplay.mId;
952             return newDisplay;
953         }
954 
turnDisplayOff()955         void turnDisplayOff() {
956             if (mExternalDisplayHelper == null) {
957                 throw new RuntimeException("No external display created");
958             }
959             mExternalDisplayHelper.turnDisplayOff();
960         }
961 
turnDisplayOn()962         void turnDisplayOn() {
963             if (mExternalDisplayHelper == null) {
964                 throw new RuntimeException("No external display created");
965             }
966             mExternalDisplayHelper.turnDisplayOn();
967         }
968 
969         @Override
close()970         public void close() {
971             if (mExternalDisplayHelper != null) {
972                 mExternalDisplayHelper.releaseDisplay();
973                 mExternalDisplayHelper = null;
974 
975                 waitForDisplayGone(d -> d.mId == mDisplayId);
976                 mDisplayId = INVALID_DISPLAY;
977             }
978         }
979     }
980 
981     public static class PrimaryDisplayStateSession implements AutoCloseable {
982 
turnScreenOff()983         void turnScreenOff() {
984             setPrimaryDisplayState(false);
985         }
986 
987         @Override
close()988         public void close() {
989             setPrimaryDisplayState(true);
990         }
991 
992         /** Turns the primary display on/off by pressing the power key */
setPrimaryDisplayState(boolean wantOn)993         private void setPrimaryDisplayState(boolean wantOn) {
994             if (wantOn) {
995                 pressWakeupButton();
996             } else {
997                 pressSleepButton();
998             }
999             VirtualDisplayHelper.waitForDefaultDisplayState(wantOn);
1000         }
1001     }
1002 }
1003