1 /* 2 * Copyright (C) 2022 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 com.android.server.inputmethod; 18 19 import static android.inputmethodservice.InputMethodService.IME_ACTIVE; 20 import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE; 21 22 import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SOFT_INPUT; 23 import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SWITCH_USER; 24 import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_SOFT_INPUT; 25 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME; 26 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT; 27 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_NOT_ALWAYS; 28 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_INVALID; 29 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME; 30 import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_SHOW_IME_IMPLICIT; 31 32 import static org.junit.Assert.assertThrows; 33 import static org.mockito.Mockito.any; 34 import static org.mockito.Mockito.anyInt; 35 import static org.mockito.Mockito.eq; 36 import static org.mockito.Mockito.mock; 37 import static org.mockito.Mockito.verify; 38 39 import android.os.Binder; 40 import android.os.IBinder; 41 import android.os.RemoteException; 42 import android.view.Display; 43 44 import androidx.test.ext.junit.runners.AndroidJUnit4; 45 46 import com.android.dx.mockito.inline.extended.ExtendedMockito; 47 import com.android.internal.inputmethod.InputBindResult; 48 import com.android.internal.inputmethod.StartInputFlags; 49 import com.android.internal.inputmethod.StartInputReason; 50 51 import org.junit.Before; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 55 /** 56 * Test the behavior of {@link DefaultImeVisibilityApplier} when performing or applying the IME 57 * visibility state. 58 * 59 * Build/Install/Run: 60 * atest FrameworksInputMethodSystemServerTests:DefaultImeVisibilityApplierTest 61 */ 62 @RunWith(AndroidJUnit4.class) 63 public class DefaultImeVisibilityApplierTest extends InputMethodManagerServiceTestBase { 64 private DefaultImeVisibilityApplier mVisibilityApplier; 65 66 @Before setUp()67 public void setUp() throws RemoteException { 68 super.setUp(); 69 mVisibilityApplier = 70 (DefaultImeVisibilityApplier) mInputMethodManagerService.getVisibilityApplier(); 71 mInputMethodManagerService.setAttachedClientForTesting( 72 mock(InputMethodManagerService.ClientState.class)); 73 } 74 75 @Test testPerformShowIme()76 public void testPerformShowIme() throws Exception { 77 synchronized (ImfLock.class) { 78 mVisibilityApplier.performShowIme(new Binder() /* showInputToken */, 79 null /* statsToken */, 0 /* showFlags */, null, SHOW_SOFT_INPUT); 80 } 81 verifyShowSoftInput(false, true, 0 /* showFlags */); 82 } 83 84 @Test testPerformHideIme()85 public void testPerformHideIme() throws Exception { 86 synchronized (ImfLock.class) { 87 mVisibilityApplier.performHideIme(new Binder() /* hideInputToken */, 88 null /* statsToken */, null, HIDE_SOFT_INPUT); 89 } 90 verifyHideSoftInput(false, true); 91 } 92 93 @Test testApplyImeVisibility_throwForInvalidState()94 public void testApplyImeVisibility_throwForInvalidState() { 95 assertThrows(IllegalArgumentException.class, 96 () -> mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_INVALID)); 97 } 98 99 @Test testApplyImeVisibility_showIme()100 public void testApplyImeVisibility_showIme() { 101 mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME); 102 verify(mMockWindowManagerInternal).showImePostLayout(eq(mWindowToken), any()); 103 } 104 105 @Test testApplyImeVisibility_hideIme()106 public void testApplyImeVisibility_hideIme() { 107 mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME); 108 verify(mMockWindowManagerInternal).hideIme(eq(mWindowToken), anyInt(), any()); 109 } 110 111 @Test testApplyImeVisibility_hideImeExplicit()112 public void testApplyImeVisibility_hideImeExplicit() throws Exception { 113 mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; 114 mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME_EXPLICIT); 115 verifyHideSoftInput(true, true); 116 } 117 118 @Test testApplyImeVisibility_hideNotAlways()119 public void testApplyImeVisibility_hideNotAlways() throws Exception { 120 mInputMethodManagerService.mImeWindowVis = IME_ACTIVE; 121 mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME_NOT_ALWAYS); 122 verifyHideSoftInput(true, true); 123 } 124 125 @Test testApplyImeVisibility_showImeImplicit()126 public void testApplyImeVisibility_showImeImplicit() throws Exception { 127 mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_SHOW_IME_IMPLICIT); 128 verifyShowSoftInput(true, true, 0 /* showFlags */); 129 } 130 131 @Test testApplyImeVisibility_hideImeFromTargetOnSecondaryDisplay()132 public void testApplyImeVisibility_hideImeFromTargetOnSecondaryDisplay() { 133 // Init a IME target client on the secondary display to show IME. 134 mInputMethodManagerService.addClient(mMockInputMethodClient, mMockRemoteInputConnection, 135 10 /* selfReportedDisplayId */); 136 mInputMethodManagerService.setAttachedClientForTesting(null); 137 startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE); 138 139 synchronized (ImfLock.class) { 140 final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked(); 141 // Verify hideIme will apply the expected displayId when the default IME 142 // visibility applier app STATE_HIDE_IME. 143 mVisibilityApplier.applyImeVisibility(mWindowToken, null, STATE_HIDE_IME); 144 verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( 145 eq(mWindowToken), eq(displayIdToShowIme), eq(null)); 146 } 147 } 148 149 @Test testShowImeScreenshot()150 public void testShowImeScreenshot() { 151 synchronized (ImfLock.class) { 152 mVisibilityApplier.showImeScreenshot(mWindowToken, Display.DEFAULT_DISPLAY, 153 null /* statsToken */); 154 } 155 156 verify(mMockImeTargetVisibilityPolicy).showImeScreenshot(eq(mWindowToken), 157 eq(Display.DEFAULT_DISPLAY)); 158 } 159 160 @Test testRemoveImeScreenshot()161 public void testRemoveImeScreenshot() { 162 synchronized (ImfLock.class) { 163 mVisibilityApplier.removeImeScreenshot(Display.DEFAULT_DISPLAY); 164 } 165 166 verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY)); 167 } 168 169 @Test testApplyImeVisibility_hideImeWhenUnbinding()170 public void testApplyImeVisibility_hideImeWhenUnbinding() { 171 mInputMethodManagerService.setAttachedClientForTesting(null); 172 startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE); 173 ExtendedMockito.spyOn(mVisibilityApplier); 174 175 synchronized (ImfLock.class) { 176 // Simulate the system hides the IME when switching IME services in different users. 177 // (e.g. unbinding the IME from the current user to the profile user) 178 final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked(); 179 mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, null, 0, null, 180 HIDE_SWITCH_USER); 181 mInputMethodManagerService.onUnbindCurrentMethodByReset(); 182 183 // Expects applyImeVisibility() -> hideIme() will be called to notify WM for syncing 184 // the IME hidden state. 185 verify(mVisibilityApplier).applyImeVisibility(eq(mWindowToken), any(), 186 eq(STATE_HIDE_IME)); 187 verify(mInputMethodManagerService.mWindowManagerInternal).hideIme( 188 eq(mWindowToken), eq(displayIdToShowIme), eq(null)); 189 } 190 } 191 startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode)192 private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) { 193 return mInputMethodManagerService.startInputOrWindowGainedFocus( 194 StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */, 195 mMockInputMethodClient /* client */, 196 windowToken /* windowToken */, 197 StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR, 198 softInputMode /* softInputMode */, 199 0 /* windowFlags */, 200 mEditorInfo /* editorInfo */, 201 mMockRemoteInputConnection /* inputConnection */, 202 mMockRemoteAccessibilityInputConnection /* remoteAccessibilityInputConnection */, 203 mTargetSdkVersion /* unverifiedTargetSdkVersion */, 204 mCallingUserId /* userId */, 205 mMockImeOnBackInvokedDispatcher /* imeDispatcher */); 206 } 207 } 208