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
This commit is contained in:
lumark
2018-09-12 17:42:53 +08:00
parent 6ea57e1b38
commit ef1965bd6d
2 changed files with 153 additions and 2 deletions

View File

@@ -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) {
}

View File

@@ -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));
}
}