From ef1965bd6d6061cb54bce305a4b99e640db19ddc Mon Sep 17 00:00:00 2001 From: lumark Date: Wed, 12 Sep 2018 17:42:53 +0800 Subject: [PATCH] Show IME in default display for some use cases. - Since IME is a kind of system decoration, for some use cases that has not set FLAG_CAN_SHOW_SYSTEM_DECORATIONS flag, it means there is no system decorations in display & need to show IME in default display. - For VR 2D display case, we show IME in default display when detected VR IME started. - Add unit tests for verifying above logics in InputMethodManagerServiceTests: - testComputeImeDisplayId_defaultDisplayId - testComputeImeDisplayId_InvalidDisplayId - testComputeImeDisplayId_VrIme - testComputeImeDisplayId_noSystemDecorationSupportDisplay - testComputeImeDisplayId_withSystemDecorationSupportDisplay Bug: 114350902 Test: atest FrameworksServicesTests:InputMethodManagerServiceTests Change-Id: Iedd71e4ddf4983f90b02dd72e471e7fa8e838fbf --- .../InputMethodManagerService.java | 53 ++++++++- .../InputMethodManagerServiceTests.java | 102 ++++++++++++++++++ 2 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 862ea9d590c44..30fec106d48d1 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -15,6 +15,7 @@ package com.android.server.inputmethod; +import static android.view.Display.DEFAULT_DISPLAY; import static android.view.Display.INVALID_DISPLAY; import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS; import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; @@ -65,6 +66,7 @@ import android.content.res.Resources; import android.content.res.TypedArray; import android.database.ContentObserver; import android.graphics.drawable.Drawable; +import android.hardware.display.DisplayManagerInternal; import android.inputmethodservice.InputMethodService; import android.net.Uri; import android.os.Binder; @@ -102,6 +104,8 @@ import android.util.Printer; import android.util.Slog; import android.util.Xml; import android.view.ContextThemeWrapper; +import android.view.Display; +import android.view.DisplayInfo; import android.view.IWindowManager; import android.view.InputChannel; import android.view.LayoutInflater; @@ -392,6 +396,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub }; private void restoreNonVrImeFromSettingsNoCheck() { + mIsVrImeStarted = false; // switch back to non-VR InputMethod from settings. synchronized (mMethodMap) { final String lastInputId = mSettings.getSelectedInputMethod(); @@ -594,6 +599,11 @@ public class InputMethodManagerService extends IInputMethodManager.Stub */ int mCurTokenDisplayId = INVALID_DISPLAY; + final ImeDisplayValidator mImeDisplayValidator; + + /** True if VR IME started by {@link #startVrInputMethodNoCheck}. */ + boolean mIsVrImeStarted; + /** * If non-null, this is the input method service we are currently connected * to. @@ -992,6 +1002,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // set this is as current inputMethod without updating settings. setInputMethodEnabledLocked(info.getId(), true); setInputMethodLocked(info.getId(), NOT_A_SUBTYPE_ID); + mIsVrImeStarted = true; break; } } @@ -1388,6 +1399,13 @@ public class InputMethodManagerService extends IInputMethodManager.Stub mIWindowManager = IWindowManager.Stub.asInterface( ServiceManager.getService(Context.WINDOW_SERVICE)); mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); + final DisplayManagerInternal displayManagerInternal = LocalServices.getService( + DisplayManagerInternal.class); + mImeDisplayValidator = (displayId) -> { + final DisplayInfo displayInfo = displayManagerInternal.getDisplayInfo(displayId); + return displayInfo != null + && (displayInfo.flags & Display.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0; + }; mCaller = new HandlerCaller(context, null, new HandlerCaller.Callback() { @Override public void executeMessage(Message msg) { @@ -1913,8 +1931,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub // Wait, the client no longer has access to the display. return InputBindResult.INVALID_DISPLAY_ID; } - // Now that the display ID is validated, we trust cs.selfReportedDisplayId for this session. - final int displayIdToShowIme = cs.selfReportedDisplayId; + // Compute the final shown display ID with validated cs.selfReportedDisplayId for this + // session & other conditions. + final int displayIdToShowIme = computeImeDisplayIdForTarget( + cs.selfReportedDisplayId, mIsVrImeStarted, mImeDisplayValidator); if (mCurClient != cs) { // Was the keyguard locked when switching over to the new client? @@ -2014,6 +2034,35 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.IME_NOT_CONNECTED; } + @FunctionalInterface + interface ImeDisplayValidator { + boolean displayCanShowIme(int displayId); + } + + /** + * Find the display where the IME should be shown. + * + * @param displayId the ID of the display where the IME client target is. + * @param isVrImeStarted {@code true} if VR IME started, {@code false} otherwise. + * @param checker instance of {@link ImeDisplayValidator} which is used for + * checking display config to adjust the final target display. + * @return The ID of the display where the IME should be shown. + */ + static int computeImeDisplayIdForTarget(int displayId, boolean isVrImeStarted, + @NonNull ImeDisplayValidator checker) { + // For VR IME, we always show in default display. + if (isVrImeStarted) { + return DEFAULT_DISPLAY; + } + if (displayId == DEFAULT_DISPLAY || displayId == INVALID_DISPLAY) { + // We always assume that the default display id suitable to show the IME window. + return DEFAULT_DISPLAY; + } + // Show IME in default display when the display with IME target doesn't support system + // decorations. + return checker.displayCanShowIme(displayId) ? displayId : DEFAULT_DISPLAY; + } + @Override public void finishInput(IInputMethodClient client) { } diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java new file mode 100644 index 0000000000000..0328621ce5d11 --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/inputmethod/InputMethodManagerServiceTests.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2018 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.inputmethod; + +import static android.view.Display.DEFAULT_DISPLAY; +import static android.view.Display.INVALID_DISPLAY; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import androidx.test.InstrumentationRegistry; +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Test; +import org.junit.runner.RunWith; + +@SmallTest +@RunWith(AndroidJUnit4.class) +public class InputMethodManagerServiceTests { + static final int SYSTEM_DECORATION_SUPPORT_DISPLAY_ID = 2; + static final int NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID = 3; + + static InputMethodManagerService.ImeDisplayValidator sChecker = + (displayId) -> { + switch (displayId) { + case SYSTEM_DECORATION_SUPPORT_DISPLAY_ID: + return true; + case NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID: + return false; + default: + throw new IllegalArgumentException("Unknown displayId=" + displayId); + } + }; + + static InputMethodManagerService.ImeDisplayValidator sMustNotBeCalledChecker = + (displayId) -> { + fail("Should not pass to display config check for this test case."); + return false; + }; + + @Test + public void testComputeImeDisplayId_defaultDisplayId() { + // Make sure that there is a short-circuit for DEFAULT_DISPLAY. + assertEquals(DEFAULT_DISPLAY, + InputMethodManagerService.computeImeDisplayIdForTarget( + DEFAULT_DISPLAY, false /* isVrImeStarted */, + sMustNotBeCalledChecker)); + } + + @Test + public void testComputeImeDisplayId_InvalidDisplayId() { + // Make sure that there is a short-circuit for INVALID_DISPLAY. + assertEquals(DEFAULT_DISPLAY, + InputMethodManagerService.computeImeDisplayIdForTarget( + INVALID_DISPLAY, false /* isVrImeStarted */, + sMustNotBeCalledChecker)); + } + + @Test + public void testComputeImeDisplayId_VrIme() { + // Make sure that there is a short-circuit for VR IME. + assertEquals(DEFAULT_DISPLAY, + InputMethodManagerService.computeImeDisplayIdForTarget( + SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, true /* isVrImeStarted */, + sMustNotBeCalledChecker)); + } + + @Test + public void testComputeImeDisplayId_noSystemDecorationSupportDisplay() { + // Presume display didn't support system decoration. + // Make sure IME displayId is DEFAULT_DISPLAY. + assertEquals(DEFAULT_DISPLAY, + InputMethodManagerService.computeImeDisplayIdForTarget( + NO_SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */, + sChecker)); + } + + @Test + public void testComputeImeDisplayId_withSystemDecorationSupportDisplay() { + // Presume display support system decoration. + // Make sure IME displayId is the same display. + assertEquals(SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, + InputMethodManagerService.computeImeDisplayIdForTarget( + SYSTEM_DECORATION_SUPPORT_DISPLAY_ID, false /* isVrImeStarted */, + sChecker)); + } +}