• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package android.server.wm;
18 
19 import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
20 import static android.server.wm.BarTestUtils.assumeHasBars;
21 import static android.server.wm.InputMethodVisibilityVerifier.expectImeInvisible;
22 import static android.server.wm.InputMethodVisibilityVerifier.expectImeVisible;
23 import static android.server.wm.MockImeHelper.createManagedMockImeSession;
24 import static android.server.wm.UiDeviceUtils.pressBackButton;
25 import static android.server.wm.WindowManagerState.STATE_RESUMED;
26 import static android.server.wm.app.Components.HOME_ACTIVITY;
27 import static android.server.wm.app.Components.SECONDARY_HOME_ACTIVITY;
28 import static android.server.wm.app.Components.SINGLE_HOME_ACTIVITY;
29 import static android.server.wm.app.Components.SINGLE_SECONDARY_HOME_ACTIVITY;
30 import static android.server.wm.app.Components.TEST_LIVE_WALLPAPER_SERVICE;
31 import static android.server.wm.app.Components.TestLiveWallpaperKeys.COMPONENT;
32 import static android.server.wm.app.Components.TestLiveWallpaperKeys.ENGINE_DISPLAY_ID;
33 import static android.view.Display.DEFAULT_DISPLAY;
34 import static android.view.WindowManager.DISPLAY_IME_POLICY_FALLBACK_DISPLAY;
35 import static android.view.WindowManager.DISPLAY_IME_POLICY_HIDE;
36 import static android.view.WindowManager.DISPLAY_IME_POLICY_LOCAL;
37 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED;
38 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
39 
40 import static com.android.cts.mockime.ImeEventStreamTestUtils.editorMatcher;
41 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectCommand;
42 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEvent;
43 import static com.android.cts.mockime.ImeEventStreamTestUtils.expectEventWithKeyValue;
44 import static com.android.cts.mockime.ImeEventStreamTestUtils.hideSoftInputMatcher;
45 import static com.android.cts.mockime.ImeEventStreamTestUtils.notExpectEvent;
46 
47 import static com.google.common.truth.Truth.assertThat;
48 import static com.google.common.truth.Truth.assertWithMessage;
49 
50 import static org.junit.Assert.assertEquals;
51 import static org.junit.Assert.assertFalse;
52 import static org.junit.Assert.assertTrue;
53 import static org.junit.Assume.assumeFalse;
54 import static org.junit.Assume.assumeTrue;
55 
56 import android.app.Activity;
57 import android.content.ComponentName;
58 import android.content.Context;
59 import android.content.ContextWrapper;
60 import android.content.Intent;
61 import android.content.res.Configuration;
62 import android.content.res.Resources;
63 import android.graphics.Bitmap;
64 import android.graphics.Rect;
65 import android.os.Bundle;
66 import android.os.SystemClock;
67 import android.platform.test.annotations.Presubmit;
68 import android.server.wm.TestJournalProvider.TestJournalContainer;
69 import android.server.wm.WindowManagerState.DisplayContent;
70 import android.server.wm.WindowManagerState.WindowState;
71 import android.server.wm.intent.Activities;
72 import android.text.TextUtils;
73 import android.view.View;
74 import android.view.Window;
75 import android.view.WindowManager;
76 import android.view.inputmethod.EditorInfo;
77 import android.view.inputmethod.InputConnection;
78 import android.view.inputmethod.InputMethodManager;
79 import android.widget.EditText;
80 import android.widget.LinearLayout;
81 
82 import com.android.compatibility.common.util.SystemUtil;
83 import com.android.compatibility.common.util.TestUtils;
84 import com.android.cts.mockime.ImeCommand;
85 import com.android.cts.mockime.ImeEventStream;
86 import com.android.cts.mockime.MockImeSession;
87 
88 import org.junit.Before;
89 import org.junit.Test;
90 
91 import java.util.List;
92 import java.util.concurrent.TimeUnit;
93 import java.util.stream.Collectors;
94 
95 /**
96  * Build/Install/Run:
97  *     atest CtsWindowManagerDeviceTestCases:MultiDisplaySystemDecorationTests
98  *
99  * This tests that verify the following should not be run for OEM device verification:
100  * Wallpaper added if display supports system decorations (and not added otherwise)
101  * Navigation bar is added if display supports system decorations (and not added otherwise)
102  * Secondary Home is shown if display supports system decorations (and not shown otherwise)
103  * IME is shown if display supports system decorations (and not shown otherwise)
104  */
105 @Presubmit
106 @android.server.wm.annotation.Group3
107 public class MultiDisplaySystemDecorationTests extends MultiDisplayTestBase {
108     final long NOT_EXPECT_TIMEOUT = TimeUnit.SECONDS.toMillis(2);
109     final long TIMEOUT = TimeUnit.SECONDS.toMillis(5);
110 
111     @Before
112     @Override
setUp()113     public void setUp() throws Exception {
114         super.setUp();
115 
116         assumeTrue(supportsMultiDisplay());
117         assumeTrue(supportsSystemDecorsOnSecondaryDisplays());
118     }
119 
120     // Wallpaper related tests
121     /**
122      * Test WallpaperService.Engine#getDisplayContext can work on secondary display.
123      */
124     @Test
testWallpaperGetDisplayContext()125     public void testWallpaperGetDisplayContext() throws Exception {
126         assumeTrue(supportsLiveWallpaper());
127 
128         final ChangeWallpaperSession wallpaperSession = createManagedChangeWallpaperSession();
129         final VirtualDisplaySession virtualDisplaySession = createManagedVirtualDisplaySession();
130 
131         TestJournalContainer.start();
132 
133         final DisplayContent newDisplay = virtualDisplaySession
134                 .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
135 
136         wallpaperSession.setWallpaperComponent(TEST_LIVE_WALLPAPER_SERVICE);
137         final String TARGET_ENGINE_DISPLAY_ID = ENGINE_DISPLAY_ID + newDisplay.mId;
138         final TestJournalProvider.TestJournal journal = TestJournalContainer.get(COMPONENT);
139         TestUtils.waitUntil("Waiting for wallpaper engine bounded", 5 /* timeoutSecond */,
140                 () -> journal.extras.getBoolean(TARGET_ENGINE_DISPLAY_ID));
141     }
142 
143     /**
144      * Tests that wallpaper shows on secondary displays.
145      */
146     @Test
testWallpaperShowOnSecondaryDisplays()147     public void testWallpaperShowOnSecondaryDisplays()  {
148         assumeTrue(supportsWallpaper());
149 
150         final ChangeWallpaperSession wallpaperSession = createManagedChangeWallpaperSession();
151 
152         final DisplayContent untrustedDisplay = createManagedExternalDisplaySession()
153                 .setPublicDisplay(true).setShowSystemDecorations(true).createVirtualDisplay();
154 
155         final DisplayContent decoredSystemDisplay = createManagedVirtualDisplaySession()
156                 .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
157 
158         final Bitmap tmpWallpaper = wallpaperSession.getTestBitmap();
159         wallpaperSession.setImageWallpaper(tmpWallpaper);
160 
161         assertTrue("Wallpaper must be displayed on system owned display with system decor flag",
162                 mWmState.waitForWithAmState(
163                         state -> isWallpaperOnDisplay(state, decoredSystemDisplay.mId),
164                         "wallpaper window to show"));
165 
166         assertFalse("Wallpaper must not be displayed on the untrusted display",
167                 isWallpaperOnDisplay(mWmState, untrustedDisplay.mId));
168     }
169 
isWallpaperOnDisplay(WindowManagerState windowManagerState, int displayId)170     private boolean isWallpaperOnDisplay(WindowManagerState windowManagerState, int displayId) {
171         return windowManagerState.getMatchingWindowType(TYPE_WALLPAPER).stream().anyMatch(
172                 w -> w.getDisplayId() == displayId);
173     }
174 
175     // Navigation bar related tests
176     // TODO(115978725): add runtime sys decor change test once we can do this.
177     /**
178      * Test that navigation bar should show on display with system decoration.
179      */
180     @Test
testNavBarShowingOnDisplayWithDecor()181     public void testNavBarShowingOnDisplayWithDecor() {
182         assumeHasBars();
183         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
184                 .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
185 
186         mWmState.waitAndAssertNavBarShownOnDisplay(newDisplay.mId);
187     }
188 
189     /**
190      * Test that navigation bar should not show on display without system decoration.
191      */
192     @Test
testNavBarNotShowingOnDisplayWithoutDecor()193     public void testNavBarNotShowingOnDisplayWithoutDecor() {
194         assumeHasBars();
195         // Wait for system decoration showing and record current nav states.
196         mWmState.waitForHomeActivityVisible();
197         final List<WindowState> expected = mWmState.getAllNavigationBarStates();
198 
199         createManagedVirtualDisplaySession().setSimulateDisplay(true)
200                 .setShowSystemDecorations(false).createDisplay();
201 
202         waitAndAssertNavBarStatesAreTheSame(expected);
203     }
204 
205     /**
206      * Test that navigation bar should not show on private display even if the display
207      * supports system decoration.
208      */
209     @Test
testNavBarNotShowingOnPrivateDisplay()210     public void testNavBarNotShowingOnPrivateDisplay() {
211         assumeHasBars();
212         // Wait for system decoration showing and record current nav states.
213         mWmState.waitForHomeActivityVisible();
214         final List<WindowState> expected = mWmState.getAllNavigationBarStates();
215 
216         createManagedExternalDisplaySession().setPublicDisplay(false)
217                 .setShowSystemDecorations(true).createVirtualDisplay();
218 
219         waitAndAssertNavBarStatesAreTheSame(expected);
220     }
221 
waitAndAssertNavBarStatesAreTheSame(List<WindowState> expected)222     private void waitAndAssertNavBarStatesAreTheSame(List<WindowState> expected) {
223         // This is used to verify that we have nav bars shown on the same displays
224         // as before the test.
225         //
226         // The strategy is:
227         // Once a display with system ui decor support is created and a nav bar shows on the
228         // display, go back to verify whether the nav bar states are unchanged to verify that no nav
229         // bars were added to a display that was added before executing this method that shouldn't
230         // have nav bars (i.e. private or without system ui decor).
231         try (final VirtualDisplaySession secondDisplaySession = new VirtualDisplaySession()) {
232             final DisplayContent supportsSysDecorDisplay = secondDisplaySession
233                     .setSimulateDisplay(true).setShowSystemDecorations(true).createDisplay();
234             mWmState.waitAndAssertNavBarShownOnDisplay(supportsSysDecorDisplay.mId);
235             // This display has finished his task. Just close it.
236         }
237 
238         mWmState.computeState();
239         final List<WindowState> result = mWmState.getAllNavigationBarStates();
240 
241         assertEquals("The number of nav bars should be the same", expected.size(), result.size());
242 
243         mWmState.getDisplays().forEach(displayContent -> {
244             List<WindowState> navWindows = expected.stream().filter(ws ->
245                     ws.getDisplayId() == displayContent.mId)
246                     .collect(Collectors.toList());
247 
248             mWmState.waitAndAssertNavBarShownOnDisplay(displayContent.mId, navWindows.size());
249         });
250     }
251 
252     // Secondary Home related tests
253     /**
254      * Tests launching a home activity on virtual display without system decoration support.
255      */
256     @Test
testLaunchHomeActivityOnSecondaryDisplayWithoutDecorations()257     public void testLaunchHomeActivityOnSecondaryDisplayWithoutDecorations() {
258         createManagedHomeActivitySession(SECONDARY_HOME_ACTIVITY);
259 
260         // Create new virtual display without system decoration support.
261         final DisplayContent newDisplay = createManagedExternalDisplaySession()
262                 .createVirtualDisplay();
263 
264         // Secondary home activity can't be launched on the display without system decoration
265         // support.
266         assertEquals("No stacks on newly launched virtual display", 0, newDisplay.mRootTasks.size());
267     }
268 
269     /** Tests launching a home activity on untrusted virtual display. */
270     @Test
testLaunchHomeActivityOnUntrustedVirtualSecondaryDisplay()271     public void testLaunchHomeActivityOnUntrustedVirtualSecondaryDisplay() {
272         createManagedHomeActivitySession(SECONDARY_HOME_ACTIVITY);
273 
274         // Create new virtual display with system decoration support flag.
275         final DisplayContent newDisplay = createManagedExternalDisplaySession()
276                 .setPublicDisplay(true).setShowSystemDecorations(true).createVirtualDisplay();
277 
278         // Secondary home activity can't be launched on the untrusted virtual display.
279         assertEquals("No stacks on untrusted virtual display", 0, newDisplay.mRootTasks.size());
280     }
281 
282     /**
283      * Tests launching a single instance home activity on virtual display with system decoration
284      * support.
285      */
286     @Test
testLaunchSingleHomeActivityOnDisplayWithDecorations()287     public void testLaunchSingleHomeActivityOnDisplayWithDecorations() {
288         createManagedHomeActivitySession(SINGLE_HOME_ACTIVITY);
289 
290         // If default home doesn't support multi-instance, default secondary home activity
291         // should be automatically launched on the new display.
292         assertSecondaryHomeResumedOnNewDisplay(getDefaultSecondaryHomeComponent());
293     }
294 
295     /**
296      * Tests launching a single instance home activity with SECONDARY_HOME on virtual display with
297      * system decoration support.
298      */
299     @Test
testLaunchSingleSecondaryHomeActivityOnDisplayWithDecorations()300     public void testLaunchSingleSecondaryHomeActivityOnDisplayWithDecorations() {
301         createManagedHomeActivitySession(SINGLE_SECONDARY_HOME_ACTIVITY);
302 
303         // If provided secondary home doesn't support multi-instance, default secondary home
304         // activity should be automatically launched on the new display.
305         assertSecondaryHomeResumedOnNewDisplay(getDefaultSecondaryHomeComponent());
306     }
307 
308     /**
309      * Tests launching a multi-instance home activity on virtual display with system decoration
310      * support.
311      */
312     @Test
testLaunchHomeActivityOnDisplayWithDecorations()313     public void testLaunchHomeActivityOnDisplayWithDecorations() {
314         createManagedHomeActivitySession(HOME_ACTIVITY);
315 
316         // If default home doesn't have SECONDARY_HOME category, default secondary home
317         // activity should be automatically launched on the new display.
318         assertSecondaryHomeResumedOnNewDisplay(getDefaultSecondaryHomeComponent());
319     }
320 
321     /**
322      * Tests launching a multi-instance home activity with SECONDARY_HOME on virtual display with
323      * system decoration support.
324      */
325     @Test
testLaunchSecondaryHomeActivityOnDisplayWithDecorations()326     public void testLaunchSecondaryHomeActivityOnDisplayWithDecorations() {
327         createManagedHomeActivitySession(SECONDARY_HOME_ACTIVITY);
328         boolean useSystemProvidedLauncher = mContext.getResources().getBoolean(
329                 Resources.getSystem().getIdentifier("config_useSystemProvidedLauncherForSecondary",
330                         "bool", "android"));
331 
332         if (useSystemProvidedLauncher) {
333             // Default secondary home activity should be automatically launched on the new display
334             // if forced by the config.
335             assertSecondaryHomeResumedOnNewDisplay(getDefaultSecondaryHomeComponent());
336         } else {
337             // Provided secondary home activity should be automatically launched on the new display.
338             assertSecondaryHomeResumedOnNewDisplay(SECONDARY_HOME_ACTIVITY);
339         }
340     }
341 
assertSecondaryHomeResumedOnNewDisplay(ComponentName homeComponentName)342     private void assertSecondaryHomeResumedOnNewDisplay(ComponentName homeComponentName) {
343         // Create new simulated display with system decoration support.
344         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
345                 .setSimulateDisplay(true)
346                 .setShowSystemDecorations(true)
347                 .createDisplay();
348 
349         waitAndAssertActivityStateOnDisplay(homeComponentName, STATE_RESUMED,
350                 newDisplay.mId, "Activity launched on secondary display must be resumed");
351 
352         tapOnDisplayCenter(newDisplay.mId);
353         assertEquals("Top activity must be home type", ACTIVITY_TYPE_HOME,
354                 mWmState.getFrontRootTaskActivityType(newDisplay.mId));
355     }
356 
357     // IME related tests
358     @Test
testImeWindowCanSwitchToDifferentDisplays()359     public void testImeWindowCanSwitchToDifferentDisplays() throws Exception {
360         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
361 
362         final MockImeSession mockImeSession = createManagedMockImeSession(this);
363         final TestActivitySession<ImeTestActivity> imeTestActivitySession =
364                 createManagedTestActivitySession();
365         final TestActivitySession<ImeTestActivity2> imeTestActivitySession2 =
366                 createManagedTestActivitySession();
367 
368         // Create a virtual display and launch an activity on it.
369         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
370                 .setShowSystemDecorations(true)
371                 .setDisplayImePolicy(DISPLAY_IME_POLICY_LOCAL)
372                 .setSimulateDisplay(true)
373                 .createDisplay();
374 
375         final ImeEventStream stream = mockImeSession.openEventStream();
376 
377         imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
378                 newDisplay.mId);
379 
380         expectEvent(stream, editorMatcher("onStartInput",
381                 imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()), TIMEOUT);
382 
383         // Make the activity to show soft input.
384         showSoftInputAndAssertImeShownOnDisplay(newDisplay.mId, imeTestActivitySession, stream);
385 
386         // Assert the configuration of the IME window is the same as the configuration of the
387         // virtual display.
388         assertImeWindowAndDisplayConfiguration(mWmState.getImeWindowState(), newDisplay);
389 
390         // Launch another activity on the default display.
391         imeTestActivitySession2.launchTestActivityOnDisplaySync(
392                 ImeTestActivity2.class, DEFAULT_DISPLAY);
393         expectEvent(stream, editorMatcher("onStartInput",
394                 imeTestActivitySession2.getActivity().mEditText.getPrivateImeOptions()), TIMEOUT);
395 
396         // Make the activity to show soft input.
397         showSoftInputAndAssertImeShownOnDisplay(DEFAULT_DISPLAY, imeTestActivitySession2, stream);
398 
399         // Assert the configuration of the IME window is the same as the configuration of the
400         // default display.
401         assertImeWindowAndDisplayConfiguration(mWmState.getImeWindowState(),
402                 mWmState.getDisplay(DEFAULT_DISPLAY));
403     }
404 
405     @Test
testImeApiForBug118341760()406     public void testImeApiForBug118341760() throws Exception {
407         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
408 
409         final long TIMEOUT_START_INPUT = TimeUnit.SECONDS.toMillis(5);
410 
411         final MockImeSession mockImeSession = createManagedMockImeSession(this);
412         final TestActivitySession<ImeTestActivityWithBrokenContextWrapper> imeTestActivitySession =
413                 createManagedTestActivitySession();
414         // Create a virtual display and launch an activity on it.
415         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
416                 .setShowSystemDecorations(true)
417                 .setSimulateDisplay(true)
418                 .createDisplay();
419         imeTestActivitySession.launchTestActivityOnDisplaySync(
420                 ImeTestActivityWithBrokenContextWrapper.class, newDisplay.mId);
421 
422         final ImeTestActivityWithBrokenContextWrapper activity =
423                 imeTestActivitySession.getActivity();
424         final ImeEventStream stream = mockImeSession.openEventStream();
425         final String privateImeOption = activity.getEditText().getPrivateImeOptions();
426         expectEvent(stream, event -> {
427             if (!TextUtils.equals("onStartInput", event.getEventName())) {
428                 return false;
429             }
430             final EditorInfo editorInfo = event.getArguments().getParcelable("editorInfo");
431             return TextUtils.equals(editorInfo.packageName, mContext.getPackageName())
432                     && TextUtils.equals(editorInfo.privateImeOptions, privateImeOption);
433         }, TIMEOUT_START_INPUT);
434 
435         imeTestActivitySession.runOnMainSyncAndWait(() -> {
436             final InputMethodManager imm = activity.getSystemService(InputMethodManager.class);
437             assertTrue("InputMethodManager.isActive() should work",
438                     imm.isActive(activity.getEditText()));
439         });
440     }
441 
442     @Test
testImeWindowCanSwitchWhenTopFocusedDisplayChange()443     public void testImeWindowCanSwitchWhenTopFocusedDisplayChange() throws Exception {
444         // If config_perDisplayFocusEnabled, the focus will not move even if touching on
445         // the Activity in the different display.
446         assumeFalse(perDisplayFocusEnabled());
447         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
448 
449         final MockImeSession mockImeSession = createManagedMockImeSession(this);
450         final TestActivitySession<ImeTestActivity> imeTestActivitySession =
451                 createManagedTestActivitySession();
452         final TestActivitySession<ImeTestActivity2> imeTestActivitySession2 =
453                 createManagedTestActivitySession();
454 
455         // Create a virtual display and launch an activity on virtual & default display.
456         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
457                 .setShowSystemDecorations(true)
458                 .setSimulateDisplay(true)
459                 .setDisplayImePolicy(DISPLAY_IME_POLICY_LOCAL)
460                 .createDisplay();
461         imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
462                 DEFAULT_DISPLAY);
463         imeTestActivitySession2.launchTestActivityOnDisplaySync(ImeTestActivity2.class,
464                 newDisplay.mId);
465 
466         final DisplayContent defDisplay = mWmState.getDisplay(DEFAULT_DISPLAY);
467         final ImeEventStream stream = mockImeSession.openEventStream();
468 
469         // Tap on the imeTestActivity task center instead of the display center because
470         // the activity might not be spanning the entire display
471         WindowManagerState.Task imeTestActivityTask = mWmState
472                 .getTaskByActivity(imeTestActivitySession.getActivity().getComponentName());
473         tapOnTaskCenter(imeTestActivityTask);
474         expectEvent(stream, editorMatcher("onStartInput",
475                 imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()), TIMEOUT);
476         showSoftInputAndAssertImeShownOnDisplay(defDisplay.mId, imeTestActivitySession, stream);
477 
478         // Tap virtual display as top focused display & request focus on EditText to show
479         // soft input.
480         tapOnDisplayCenter(newDisplay.mId);
481         expectEvent(stream, editorMatcher("onStartInput",
482                 imeTestActivitySession2.getActivity().mEditText.getPrivateImeOptions()), TIMEOUT);
483         showSoftInputAndAssertImeShownOnDisplay(newDisplay.mId, imeTestActivitySession2, stream);
484 
485         // Tap on the imeTestActivity task center instead of the display center because
486         // the activity might not be spanning the entire display
487         imeTestActivityTask = mWmState
488                 .getTaskByActivity(imeTestActivitySession.getActivity().getComponentName());
489         tapOnTaskCenter(imeTestActivityTask);
490         expectEvent(stream, editorMatcher("onStartInput",
491                 imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()), TIMEOUT);
492         showSoftInputAndAssertImeShownOnDisplay(defDisplay.mId, imeTestActivitySession, stream);
493     }
494 
495     /**
496      * Test that the IME can be shown in a different display (actually the default display) than
497      * the display on which the target IME application is shown.  Then test several basic operations
498      * in {@link InputConnection}.
499      */
500     @Test
testCrossDisplayBasicImeOperations()501     public void testCrossDisplayBasicImeOperations() throws Exception {
502         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
503 
504         final MockImeSession mockImeSession = createManagedMockImeSession(this);
505         final TestActivitySession<ImeTestActivity> imeTestActivitySession =
506                 createManagedTestActivitySession();
507 
508         // Create a virtual display by app and assume the display should not show IME window.
509         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
510                 .setPublicDisplay(true)
511                 .createDisplay();
512         SystemUtil.runWithShellPermissionIdentity(
513                 () -> assertTrue("Display should not support showing IME window",
514                         mTargetContext.getSystemService(WindowManager.class)
515                                 .getDisplayImePolicy(newDisplay.mId)
516                                 == DISPLAY_IME_POLICY_FALLBACK_DISPLAY));
517 
518         // Launch Ime test activity in virtual display.
519         imeTestActivitySession.launchTestActivityOnDisplay(ImeTestActivity.class,
520                 newDisplay.mId);
521         final ImeEventStream stream = mockImeSession.openEventStream();
522 
523         // Expect onStartInput would be executed when user tapping on the
524         // non-system created display intentionally.
525         tapAndAssertEditorFocusedOnImeActivity(imeTestActivitySession, newDisplay.mId);
526         expectEvent(stream, editorMatcher("onStartInput",
527                 imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()), TIMEOUT);
528 
529         // Verify the activity to show soft input on the default display.
530         showSoftInputAndAssertImeShownOnDisplay(DEFAULT_DISPLAY, imeTestActivitySession, stream);
531 
532         // Commit text & make sure the input texts should be delivered to focused EditText on
533         // virtual display.
534         final EditText editText = imeTestActivitySession.getActivity().mEditText;
535         final String commitText = "test commit";
536         expectCommand(stream, mockImeSession.callCommitText(commitText, 1), TIMEOUT);
537         imeTestActivitySession.runOnMainAndAssertWithTimeout(
538                 () -> TextUtils.equals(commitText, editText.getText()), TIMEOUT,
539                 "The input text should be delivered");
540 
541         // Since the IME and the IME target app are running in different displays,
542         // InputConnection#requestCursorUpdates() is not supported and it should return false.
543         // See InputMethodServiceTest#testOnUpdateCursorAnchorInfo() for the normal scenario.
544         final ImeCommand callCursorUpdates = mockImeSession.callRequestCursorUpdates(
545                 InputConnection.CURSOR_UPDATE_IMMEDIATE);
546         assertFalse(expectCommand(stream, callCursorUpdates, TIMEOUT).getReturnBooleanValue());
547     }
548 
549     /**
550      * Test that the IME can be hidden with the {@link WindowManager#DISPLAY_IME_POLICY_HIDE} flag.
551      */
552     @Test
testDisplayPolicyImeHideImeOperation()553     public void testDisplayPolicyImeHideImeOperation() throws Exception {
554         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
555 
556         final MockImeSession mockImeSession = createManagedMockImeSession(this);
557         final TestActivitySession<ImeTestActivity> imeTestActivitySession =
558                 createManagedTestActivitySession();
559 
560         // Create a virtual display and launch an activity on virtual display.
561         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
562                 .setShowSystemDecorations(true)
563                 .setDisplayImePolicy(DISPLAY_IME_POLICY_HIDE)
564                 .setSimulateDisplay(true)
565                 .createDisplay();
566 
567         // Launch Ime test activity and initial the editor focus on virtual display.
568         imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
569                 newDisplay.mId);
570 
571         // Verify the activity is launched to the secondary display.
572         final ComponentName imeTestActivityName =
573                 imeTestActivitySession.getActivity().getComponentName();
574         assertThat(mWmState.hasActivityInDisplay(newDisplay.mId, imeTestActivityName)).isTrue();
575 
576         // Verify invoking showSoftInput will be ignored when the display with the HIDE policy.
577         final ImeEventStream stream = mockImeSession.openEventStream();
578         imeTestActivitySession.runOnMainSyncAndWait(
579                 imeTestActivitySession.getActivity()::showSoftInput);
580         notExpectEvent(stream, editorMatcher("showSoftInput",
581                 imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
582                 NOT_EXPECT_TIMEOUT);
583     }
584 
585     /**
586      * A regression test for Bug 273630528.
587      *
588      * Test that the IME on the editor activity with embedded in virtual display will be hidden
589      * after pressing the back key.
590      */
591     @Test
testHideImeWhenImeTargetOnEmbeddedVirtualDisplay()592     public void testHideImeWhenImeTargetOnEmbeddedVirtualDisplay() throws Exception {
593         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
594 
595         final VirtualDisplaySession session = createManagedVirtualDisplaySession();
596         final MockImeSession imeSession = createManagedMockImeSession(this);
597         final TestActivitySession<ImeTestActivity> imeActivitySession =
598                 createManagedTestActivitySession();
599 
600         // Setup a virtual display embedded on an activity.
601         final WindowManagerState.DisplayContent dc = session
602                 .setPublicDisplay(true)
603                 .setSupportsTouch(true)
604                 .createDisplay();
605 
606         // Launch a test activity on that virtual display and show IME by tapping the editor.
607         imeActivitySession.launchTestActivityOnDisplay(ImeTestActivity.class, dc.mId);
608         tapAndAssertEditorFocusedOnImeActivity(imeActivitySession, dc.mId);
609         final ImeEventStream stream = imeSession.openEventStream();
610         final String marker = imeActivitySession.getActivity().mEditText.getPrivateImeOptions();
611         expectEvent(stream, editorMatcher("onStartInput", marker), TIMEOUT);
612 
613         // Expect soft-keyboard becomes visible after requesting show IME.
614         showSoftInputAndAssertImeShownOnDisplay(DEFAULT_DISPLAY, imeActivitySession, stream);
615         expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible",
616                 View.VISIBLE, TIMEOUT);
617         expectImeVisible(TIMEOUT);
618 
619         // Pressing back key, expect soft-keyboard will become invisible.
620         pressBackButton();
621         expectEvent(stream, hideSoftInputMatcher(), TIMEOUT);
622         expectEventWithKeyValue(stream, "onWindowVisibilityChanged", "visible",
623                 View.GONE, TIMEOUT);
624         expectImeInvisible(TIMEOUT);
625     }
626 
627     /**
628      * Test that the IME remains hidden with the {@link WindowManager#DISPLAY_IME_POLICY_HIDE} flag
629      * if the user taps the EditText on displays with no system decorations.
630      */
631     @Test
testDisplayPolicyImeHideImeNoSystemDecorations()632     public void testDisplayPolicyImeHideImeNoSystemDecorations() throws Exception {
633         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
634 
635         final MockImeSession mockImeSession = createManagedMockImeSession(this);
636         final ImeEventStream stream = mockImeSession.openEventStream();
637 
638         // Create a virtual display with the policy to hide the IME.
639         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
640                 .setShowSystemDecorations(false)
641                 .setDisplayImePolicy(DISPLAY_IME_POLICY_HIDE)
642                 .setSimulateDisplay(true)
643                 .createDisplay();
644 
645         SystemUtil.runWithShellPermissionIdentity(
646                 () -> assertTrue("Display should not support showing IME window",
647                         mTargetContext.getSystemService(WindowManager.class)
648                                 .getDisplayImePolicy(newDisplay.mId)
649                                 == DISPLAY_IME_POLICY_HIDE));
650 
651         final TestActivitySession<ImeTestActivity> imeTestActivitySession =
652                 createManagedTestActivitySession();
653 
654         // Launch Ime test activity and initial the editor focus on virtual display.
655         imeTestActivitySession.launchTestActivityOnDisplay(ImeTestActivity.class,
656                 newDisplay.mId);
657 
658         // Expect no onStartInput and the activity does not show soft input when user taps the
659         // editor on the display with the HIDE policy.
660         tapAndAssertEditorFocusedOnImeActivity(imeTestActivitySession, newDisplay.mId);
661         notExpectEvent(stream, editorMatcher("onStartInput",
662                 imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
663                 NOT_EXPECT_TIMEOUT);
664         InputMethodVisibilityVerifier.expectImeInvisible(NOT_EXPECT_TIMEOUT);
665     }
666 
667     @Test
testImeWindowCanShownWhenActivityMovedToDisplay()668     public void testImeWindowCanShownWhenActivityMovedToDisplay() throws Exception {
669         // If config_perDisplayFocusEnabled, the focus will not move even if touching on
670         // the Activity in the different display.
671         assumeFalse(perDisplayFocusEnabled());
672         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
673 
674         // Launch a regular activity on default display at the test beginning to prevent the test
675         // may mis-touch the launcher icon that breaks the test expectation.
676         final TestActivitySession<Activities.RegularActivity> testActivitySession =
677                 createManagedTestActivitySession();
678         testActivitySession.launchTestActivityOnDisplaySync(Activities.RegularActivity.class,
679                 DEFAULT_DISPLAY);
680 
681         // Create a virtual display and launch an activity on virtual display.
682         final DisplayContent newDisplay = createManagedVirtualDisplaySession()
683                 .setShowSystemDecorations(true)
684                 .setDisplayImePolicy(DISPLAY_IME_POLICY_LOCAL)
685                 .setSimulateDisplay(true)
686                 .createDisplay();
687 
688         // Leverage MockImeSession to ensure at least an IME exists as default.
689         final MockImeSession mockImeSession = createManagedMockImeSession(this);
690         final TestActivitySession<ImeTestActivity> imeTestActivitySession =
691                 createManagedTestActivitySession();
692         // Launch Ime test activity and initial the editor focus on virtual display.
693         imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
694                 newDisplay.mId);
695 
696         // Verify the activity is launched to the secondary display.
697         final ComponentName imeTestActivityName =
698                 imeTestActivitySession.getActivity().getComponentName();
699         assertThat(mWmState.hasActivityInDisplay(newDisplay.mId, imeTestActivityName)).isTrue();
700 
701         // Tap default display, assume a pointer-out-side event will happened to change the top
702         // display.
703         final DisplayContent defDisplay = mWmState.getDisplay(DEFAULT_DISPLAY);
704         tapOnDisplayCenter(defDisplay.mId);
705         mWmState.waitForAppTransitionIdleOnDisplay(DEFAULT_DISPLAY);
706         mWmState.assertValidity();
707 
708         // Reparent ImeTestActivity from virtual display to default display.
709         getLaunchActivityBuilder()
710                 .setUseInstrumentation()
711                 .setTargetActivity(imeTestActivitySession.getActivity().getComponentName())
712                 .setIntentFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
713                 .allowMultipleInstances(false)
714                 .setDisplayId(DEFAULT_DISPLAY).execute();
715         waitAndAssertTopResumedActivity(imeTestActivitySession.getActivity().getComponentName(),
716                 DEFAULT_DISPLAY, "Activity launched on default display and on top");
717 
718         // Activity is no longer on the secondary display
719         assertThat(mWmState.hasActivityInDisplay(newDisplay.mId, imeTestActivityName)).isFalse();
720 
721         // Tap on the imeTestActivity task center instead of the display center because
722         // the activity might not be spanning the entire display
723         final ImeEventStream stream = mockImeSession.openEventStream();
724         final WindowManagerState.Task testActivityTask = mWmState
725                 .getTaskByActivity(imeTestActivitySession.getActivity().getComponentName());
726         tapOnTaskCenter(testActivityTask);
727         expectEvent(stream, editorMatcher("onStartInput",
728                 imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()), TIMEOUT);
729 
730         // Verify the activity shows soft input on the default display.
731         showSoftInputAndAssertImeShownOnDisplay(DEFAULT_DISPLAY, imeTestActivitySession, stream);
732     }
733 
734     @Test
testNoConfigurationChangedWhenSwitchBetweenTwoIdenticalDisplays()735     public void testNoConfigurationChangedWhenSwitchBetweenTwoIdenticalDisplays() throws Exception {
736         // If config_perDisplayFocusEnabled, the focus will not move even if touching on
737         // the Activity in the different display.
738         assumeFalse(perDisplayFocusEnabled());
739         assumeTrue(MSG_NO_MOCK_IME, supportsInstallableIme());
740 
741         // Create two displays with the same display metrics
742         final List<DisplayContent> newDisplays = createManagedVirtualDisplaySession()
743                 .setShowSystemDecorations(true)
744                 .setDisplayImePolicy(DISPLAY_IME_POLICY_LOCAL)
745                 .setSimulateDisplay(true)
746                 .setResizeDisplay(false)
747                 .createDisplays(2);
748         final DisplayContent firstDisplay = newDisplays.get(0);
749         final DisplayContent secondDisplay = newDisplays.get(1);
750 
751         // Skip if the test environment somehow didn't create 2 displays with identical size.
752         assumeTrue("Skip the test if the size of the created displays aren't identical",
753                 firstDisplay.getDisplayRect().equals(secondDisplay.getDisplayRect()));
754 
755         // Make firstDisplay the top focus display.
756         tapOnDisplayCenter(firstDisplay.mId);
757 
758         mWmState.waitForWithAmState(state -> state.getFocusedDisplayId() == firstDisplay.mId,
759                 "First display must be top focused.");
760 
761         // Initialize IME test environment
762         final MockImeSession mockImeSession = createManagedMockImeSession(this);
763         final TestActivitySession<ImeTestActivity> imeTestActivitySession =
764                 createManagedTestActivitySession();
765         ImeEventStream stream = mockImeSession.openEventStream();
766         // Filter out onConfigurationChanged events in case that IME is moved from the default
767         // display to the firstDisplay.
768         ImeEventStream configChangeVerifyStream = clearOnConfigurationChangedFromStream(stream);
769 
770         imeTestActivitySession.launchTestActivityOnDisplaySync(ImeTestActivity.class,
771                 firstDisplay.mId);
772         imeTestActivitySession.runOnMainSyncAndWait(
773                 imeTestActivitySession.getActivity()::showSoftInput);
774 
775         waitOrderedImeEventsThenAssertImeShown(stream, firstDisplay.mId,
776                 editorMatcher("onStartInput",
777                         imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
778                 event -> "showSoftInput".equals(event.getEventName()));
779         try {
780             // Launch Ime must not lead to screen size changes.
781             waitAndAssertImeNoScreenSizeChanged(configChangeVerifyStream);
782 
783             final Rect currentBoundsOnFirstDisplay = expectCommand(stream,
784                     mockImeSession.callGetCurrentWindowMetricsBounds(), TIMEOUT)
785                     .getReturnParcelableValue();
786 
787             // Clear onConfigurationChanged events before IME moves to the secondary display to
788             // prevent flaky because IME may receive configuration updates which we don't care
789             // about. An example is CONFIG_KEYBOARD_HIDDEN.
790             configChangeVerifyStream = clearOnConfigurationChangedFromStream(stream);
791 
792             // Tap secondDisplay to change it to the top focused display.
793             tapOnDisplayCenter(secondDisplay.mId);
794 
795             // Move ImeTestActivity from firstDisplay to secondDisplay.
796             getLaunchActivityBuilder()
797                     .setUseInstrumentation()
798                     .setTargetActivity(imeTestActivitySession.getActivity().getComponentName())
799                     .setIntentFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
800                     .allowMultipleInstances(false)
801                     .setDisplayId(secondDisplay.mId).execute();
802 
803             // Make sure ImeTestActivity is move from the firstDisplay to the secondDisplay
804             waitAndAssertTopResumedActivity(imeTestActivitySession.getActivity().getComponentName(),
805                     secondDisplay.mId, "ImeTestActivity must be top-resumed on display#"
806                             + secondDisplay.mId);
807             assertThat(mWmState.hasActivityInDisplay(firstDisplay.mId,
808                     imeTestActivitySession.getActivity().getComponentName())).isFalse();
809 
810             // Show soft input again to trigger IME movement.
811             imeTestActivitySession.runOnMainSyncAndWait(
812                     imeTestActivitySession.getActivity()::showSoftInput);
813 
814             waitOrderedImeEventsThenAssertImeShown(stream, secondDisplay.mId,
815                     editorMatcher("onStartInput",
816                             imeTestActivitySession.getActivity().mEditText.getPrivateImeOptions()),
817                     event -> "showSoftInput".equals(event.getEventName()));
818 
819             // Moving IME to the display with the same display metrics must not lead to
820             // screen size changes.
821             waitAndAssertImeNoScreenSizeChanged(configChangeVerifyStream);
822 
823             final Rect currentBoundsOnSecondDisplay = expectCommand(stream,
824                     mockImeSession.callGetCurrentWindowMetricsBounds(), TIMEOUT)
825                     .getReturnParcelableValue();
826 
827             assertWithMessage("The current WindowMetrics bounds of IME must not be changed.")
828                     .that(currentBoundsOnFirstDisplay).isEqualTo(currentBoundsOnSecondDisplay);
829         } catch (AssertionError e) {
830             mWmState.computeState();
831             final Rect displayRect1 = mWmState.getDisplay(firstDisplay.mId).getDisplayRect();
832             final Rect displayRect2 = mWmState.getDisplay(secondDisplay.mId).getDisplayRect();
833             assumeTrue("Skip test since the size of one or both displays happens unexpected change",
834                     displayRect1.equals(displayRect2));
835             throw e;
836         }
837     }
838 
839     public static class ImeTestActivity extends Activity {
840         EditText mEditText;
841 
842         @Override
onCreate(Bundle icicle)843         protected void onCreate(Bundle icicle) {
844             super.onCreate(icicle);
845             mEditText = new EditText(this);
846             // Set private IME option for editorMatcher to identify which TextView received
847             // onStartInput event.
848             resetPrivateImeOptionsIdentifier();
849             final LinearLayout layout = new LinearLayout(this);
850             layout.setOrientation(LinearLayout.VERTICAL);
851             layout.addView(mEditText);
852             mEditText.requestFocus();
853             // SOFT_INPUT_STATE_UNSPECIFIED may produced unexpected behavior for CTS. To make tests
854             // deterministic, using SOFT_INPUT_STATE_UNCHANGED instead.
855             setUnchangedSoftInputState();
856             setContentView(layout);
857         }
858 
showSoftInput()859         void showSoftInput() {
860             final InputMethodManager imm = getSystemService(InputMethodManager.class);
861             imm.showSoftInput(mEditText, 0);
862         }
863 
resetPrivateImeOptionsIdentifier()864         void resetPrivateImeOptionsIdentifier() {
865             mEditText.setPrivateImeOptions(
866                     getClass().getName() + "/" + Long.toString(SystemClock.elapsedRealtimeNanos()));
867         }
868 
setUnchangedSoftInputState()869         private void setUnchangedSoftInputState() {
870             final Window window = getWindow();
871             final int currentSoftInputMode = window.getAttributes().softInputMode;
872             final int newSoftInputMode =
873                     (currentSoftInputMode & ~WindowManager.LayoutParams.SOFT_INPUT_MASK_STATE)
874                             | SOFT_INPUT_STATE_UNCHANGED;
875             window.setSoftInputMode(newSoftInputMode);
876         }
877     }
878 
879     public static class ImeTestActivity2 extends ImeTestActivity { }
880 
881     public static final class ImeTestActivityWithBrokenContextWrapper extends Activity {
882         private EditText mEditText;
883 
884         /**
885          * Emulates the behavior of certain {@link ContextWrapper} subclasses we found in the wild.
886          *
887          * <p> Certain {@link ContextWrapper} subclass in the wild delegate method calls to
888          * ApplicationContext except for {@link #getSystemService(String)}.</p>
889          *
890          **/
891         private static final class Bug118341760ContextWrapper extends ContextWrapper {
892             private final Context mOriginalContext;
893 
Bug118341760ContextWrapper(Context base)894             Bug118341760ContextWrapper(Context base) {
895                 super(base.getApplicationContext());
896                 mOriginalContext = base;
897             }
898 
899             /**
900              * Emulates the behavior of {@link ContextWrapper#getSystemService(String)} of certain
901              * {@link ContextWrapper} subclasses we found in the wild.
902              *
903              * @param name The name of the desired service.
904              * @return The service or {@link null} if the name does not exist.
905              */
906             @Override
getSystemService(String name)907             public Object getSystemService(String name) {
908                 return mOriginalContext.getSystemService(name);
909             }
910         }
911 
912         @Override
onCreate(Bundle icicle)913         protected void onCreate(Bundle icicle) {
914             super.onCreate(icicle);
915             mEditText = new EditText(new Bug118341760ContextWrapper(this));
916             // Use SystemClock.elapsedRealtimeNanos()) as a unique ID of this edit text.
917             mEditText.setPrivateImeOptions(Long.toString(SystemClock.elapsedRealtimeNanos()));
918             final LinearLayout layout = new LinearLayout(this);
919             layout.setOrientation(LinearLayout.VERTICAL);
920             layout.addView(mEditText);
921             mEditText.requestFocus();
922             setContentView(layout);
923         }
924 
getEditText()925         EditText getEditText() {
926             return mEditText;
927         }
928     }
929 
assertImeWindowAndDisplayConfiguration( WindowState imeWinState, DisplayContent display)930     private void assertImeWindowAndDisplayConfiguration(
931             WindowState imeWinState, DisplayContent display) {
932         // The IME window should inherit the configuration from the IME DisplayArea.
933         final WindowManagerState.DisplayArea imeContainerDisplayArea = display.getImeContainer();
934         final Configuration configurationForIme = imeWinState.mMergedOverrideConfiguration;
935         final Configuration configurationForImeContainer =
936                 imeContainerDisplayArea.mMergedOverrideConfiguration;
937         final int displayDensityDpiForIme = configurationForIme.densityDpi;
938         final int displayDensityDpiForImeContainer = configurationForImeContainer.densityDpi;
939         final Rect displayBoundsForIme = configurationForIme.windowConfiguration.getBounds();
940         final Rect displayBoundsForImeContainer =
941                 configurationForImeContainer.windowConfiguration.getBounds();
942 
943         assertEquals("Display density not the same",
944                 displayDensityDpiForImeContainer, displayDensityDpiForIme);
945         assertEquals("Display bounds not the same",
946                 displayBoundsForImeContainer, displayBoundsForIme);
947     }
948 
tapAndAssertEditorFocusedOnImeActivity( TestActivitySession<? extends ImeTestActivity> activitySession, int expectDisplayId)949     private void tapAndAssertEditorFocusedOnImeActivity(
950             TestActivitySession<? extends ImeTestActivity> activitySession, int expectDisplayId) {
951         final int[] location = new int[2];
952         waitAndAssertActivityStateOnDisplay(activitySession.getActivity().getComponentName(),
953                 STATE_RESUMED, expectDisplayId,
954                 "ImeActivity failed to appear on display#" + expectDisplayId);
955         activitySession.runOnMainSyncAndWait(() -> {
956             final EditText editText = activitySession.getActivity().mEditText;
957             editText.getLocationOnScreen(location);
958         });
959         final ComponentName expectComponent = activitySession.getActivity().getComponentName();
960         tapOnDisplaySync(location[0], location[1], expectDisplayId);
961         mWmState.computeState(activitySession.getActivity().getComponentName());
962         mWmState.assertFocusedAppOnDisplay("Activity not focus on the display", expectComponent,
963                 expectDisplayId);
964     }
965 
showSoftInputAndAssertImeShownOnDisplay(int displayId, TestActivitySession<? extends ImeTestActivity> activitySession, ImeEventStream stream)966     private void showSoftInputAndAssertImeShownOnDisplay(int displayId,
967             TestActivitySession<? extends ImeTestActivity> activitySession, ImeEventStream stream)
968             throws Exception {
969         activitySession.runOnMainSyncAndWait(
970                 activitySession.getActivity()::showSoftInput);
971         expectEvent(stream, editorMatcher("onStartInputView",
972                 activitySession.getActivity().mEditText.getPrivateImeOptions()), TIMEOUT);
973         // Assert the IME is shown on the expected display.
974         mWmState.waitAndAssertImeWindowShownOnDisplay(displayId);
975     }
976 }
977