From a97ad615016db2ee19b8918d8f37baf6a65a310a Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Wed, 9 Feb 2022 23:05:42 +0800 Subject: [PATCH 1/2] Introduce ImePlatformCompatUtils To easier maintain more platform compat behavior changes in IMF side, with wrapping IPlatformCompat checkings into a new utility class that used by IMMS to check if the given state is valid to enable/disable IME behaviors on the platform side. This is a mechanical refactoring and should not have any behavior changes. Bug: 16825711 Test: Build Test: atest CtsInputMethodTestCases Change-Id: Icc19e52567de58cd72f2abba1b0aa6dd176995fd --- .../inputmethod/ImePlatformCompatUtils.java | 53 +++++++++++++++++++ .../InputMethodManagerService.java | 18 ++----- 2 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java diff --git a/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java new file mode 100644 index 0000000000000..ae77b9dc5c821 --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2022 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.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; + +import android.content.Context; +import android.os.RemoteException; +import android.os.ServiceManager; + +import com.android.internal.compat.IPlatformCompat; + +/** + * A utility class used by {@link InputMethodManagerService} to manage the platform + * compatibility changes on IMF (Input Method Framework) side. + */ +final class ImePlatformCompatUtils { + private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface( + ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + + /** + * Whether to finish the {@link android.view.inputmethod.InputConnection} when the device + * becomes {@link android.os.PowerManager#isInteractive non-interactive}. + * + * @param imeUid The uid of the IME application + */ + public boolean shouldFinishInputWithReportToIme(int imeUid) { + return isChangeEnabledByUid(FINISH_INPUT_NO_FALLBACK_CONNECTION, imeUid); + } + + private boolean isChangeEnabledByUid(long changeFlag, int uid) { + boolean result = false; + try { + result = mPlatformCompat.isChangeEnabledByUid(changeFlag, uid); + } catch (RemoteException e) { + } + return result; + } +} diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 0b7e39136feb3..afcbd8984e1da 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -15,7 +15,6 @@ package com.android.server.inputmethod; -import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_CRITICAL; import static android.os.IServiceManager.DUMP_FLAG_PRIORITY_NORMAL; import static android.os.IServiceManager.DUMP_FLAG_PROTO; @@ -148,7 +147,6 @@ import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import com.android.internal.annotations.GuardedBy; -import com.android.internal.compat.IPlatformCompat; import com.android.internal.content.PackageMonitor; import com.android.internal.infra.AndroidFuture; import com.android.internal.inputmethod.DirectBootAwareness; @@ -279,6 +277,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final WindowManagerInternal mWindowManagerInternal; final PackageManagerInternal mPackageManagerInternal; final InputManagerInternal mInputManagerInternal; + final ImePlatformCompatUtils mImePlatformCompatUtils; final boolean mHasFeature; private final ArrayMap> mAdditionalSubtypeMap = new ArrayMap<>(); @@ -691,8 +690,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub */ boolean mIsInteractive = true; - private final IPlatformCompat mPlatformCompat; - int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT; /** @@ -1627,6 +1624,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mWindowManagerInternal = LocalServices.getService(WindowManagerInternal.class); mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); mInputManagerInternal = LocalServices.getService(InputManagerInternal.class); + mImePlatformCompatUtils = new ImePlatformCompatUtils(); mImeDisplayValidator = mWindowManagerInternal::getDisplayImePolicy; mAppOpsManager = mContext.getSystemService(AppOpsManager.class); mUserManager = mContext.getSystemService(UserManager.class); @@ -1634,8 +1632,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub mAccessibilityManager = AccessibilityManager.getInstance(context); mHasFeature = context.getPackageManager().hasSystemFeature( PackageManager.FEATURE_INPUT_METHODS); - mPlatformCompat = IPlatformCompat.Stub.asInterface( - ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); + mSlotIme = mContext.getString(com.android.internal.R.string.status_bar_ime); Bundle extras = new Bundle(); @@ -4728,14 +4725,9 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Inform the current client of the change in active status if (mCurClient != null && mCurClient.client != null) { - boolean reportToImeController = false; - try { - reportToImeController = mPlatformCompat.isChangeEnabledByUid( - FINISH_INPUT_NO_FALLBACK_CONNECTION, getCurMethodUidLocked()); - } catch (RemoteException e) { - } scheduleSetActiveToClient(mCurClient, mIsInteractive, mInFullscreenMode, - reportToImeController); + mImePlatformCompatUtils.shouldFinishInputWithReportToIme( + getCurMethodUidLocked())); } } } From df5fc6f727b698ca40f4aecad16a12513ecd12b6 Mon Sep 17 00:00:00 2001 From: Ming-Shin Lu Date: Thu, 10 Feb 2022 00:40:34 +0800 Subject: [PATCH 2/2] Clear SHOW_FORCED flag when the app's targetSdk >= T As the legacy flag SHOW_FORCED when calling showSoftInput keeps remaining soft-keyboard visible, even though switching focus to another application the situation still remains, unless someone calls hideSoftInputFromWindow explicitly. In case SHOW_FORCED flag affects the next client to keep IME visible and hard to handle for the app developer, we introduce a platform compatibility flag, when enabled, if the current client is leaving due to the next focused client, in IMMS side will clear mShowForced flag when the next client's targetSdkVersion is T or higher. Also, deprecated SHOW_FORCED flag with updating the reason in the documentation. Fix: 214016041 Test: atest KeyboardVisibilityControlTest#\ testShowSoftInputWithShowForcedFlagWhenAppIsLeaving Change-Id: I47efe5cb02ba69cbe018619e5bdc8f3995a55b4b --- core/api/current.txt | 2 +- core/api/test-current.txt | 1 + .../view/inputmethod/InputMethodManager.java | 24 +++++++++++++++++++ .../inputmethod/ImePlatformCompatUtils.java | 11 +++++++++ .../InputMethodManagerService.java | 8 +++++++ 5 files changed, 45 insertions(+), 1 deletion(-) diff --git a/core/api/current.txt b/core/api/current.txt index ddfbb44e44a66..4ad6da69a7998 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -53105,7 +53105,7 @@ package android.view.inputmethod { field public static final int RESULT_SHOWN = 2; // 0x2 field public static final int RESULT_UNCHANGED_HIDDEN = 1; // 0x1 field public static final int RESULT_UNCHANGED_SHOWN = 0; // 0x0 - field public static final int SHOW_FORCED = 2; // 0x2 + field @Deprecated public static final int SHOW_FORCED = 2; // 0x2 field public static final int SHOW_IMPLICIT = 1; // 0x1 } diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 9757b2ff318f9..f0a8b9e68b708 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -3082,6 +3082,7 @@ package android.view.inputmethod { method @NonNull @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public java.util.List getInputMethodListAsUser(int); method public boolean hasActiveInputConnection(@Nullable android.view.View); method public boolean isInputMethodPickerShown(); + field public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // 0xcc1a029L } } diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 27174630bfa33..017b8aa3cf6c6 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -46,6 +46,8 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.ActivityThread; +import android.compat.annotation.ChangeId; +import android.compat.annotation.EnabledSince; import android.compat.annotation.UnsupportedAppUsage; import android.content.ComponentName; import android.content.ContentResolver; @@ -356,6 +358,21 @@ public final class InputMethodManager { /** @hide */ public static final int SHOW_IM_PICKER_MODE_EXCLUDE_AUXILIARY_SUBTYPES = 2; + /** + * Clear {@link #SHOW_FORCED} flag when the next IME focused application changed. + * + *

+ * Note that when this flag enabled in server side, {@link #SHOW_FORCED} will no longer + * affect the next focused application to keep showing IME, in case of unexpected IME visible + * when the next focused app isn't be the IME requester.

+ * + * @hide + */ + @TestApi + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + public static final long CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING = 214016041L; // This is a bug id. + @UnsupportedAppUsage final IInputMethodManager mService; final Looper mMainLooper; @@ -1646,7 +1663,14 @@ public final class InputMethodManager { * Flag for {@link #showSoftInput} to indicate that the user has forced * the input method open (such as by long-pressing menu) so it should * not be closed until they explicitly do so. + * + * @deprecated Use {@link #showSoftInput} without this flag instead. Using this flag can lead + * to the soft input remaining visible even when the calling application is closed. The + * use of this flag can make the soft input remains visible globally. Starting in + * {@link Build.VERSION_CODES#TIRAMISU Android T}, this flag only has an effect while the + * caller is currently focused. */ + @Deprecated public static final int SHOW_FORCED = 0x0002; /** diff --git a/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java index ae77b9dc5c821..83ca16d72c9b8 100644 --- a/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java +++ b/services/core/java/com/android/server/inputmethod/ImePlatformCompatUtils.java @@ -17,6 +17,7 @@ package com.android.server.inputmethod; import static android.inputmethodservice.InputMethodService.FINISH_INPUT_NO_FALLBACK_CONNECTION; +import static android.view.inputmethod.InputMethodManager.CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING; import android.content.Context; import android.os.RemoteException; @@ -42,6 +43,16 @@ final class ImePlatformCompatUtils { return isChangeEnabledByUid(FINISH_INPUT_NO_FALLBACK_CONNECTION, imeUid); } + /** + * Whether to clear {@link android.view.inputmethod.InputMethodManager#SHOW_FORCED} flag + * when the next IME focused application changed. + * + * @param clientUid The uid of the app + */ + public boolean shouldClearShowForcedFlag(int clientUid) { + return isChangeEnabledByUid(CLEAR_SHOW_FORCED_FLAG_WHEN_LEAVING, clientUid); + } + private boolean isChangeEnabledByUid(long changeFlag, int uid) { boolean result = false; try { diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index afcbd8984e1da..eb1de2a9bf915 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3617,6 +3617,14 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub return InputBindResult.INVALID_USER; } + final boolean shouldClearFlag = mImePlatformCompatUtils.shouldClearShowForcedFlag(cs.uid); + // In case mShowForced flag affects the next client to keep IME visible, when the current + // client is leaving due to the next focused client, we clear mShowForced flag when the + // next client's targetSdkVersion is T or higher. + if (mCurFocusedWindow != windowToken && mShowForced && shouldClearFlag) { + mShowForced = false; + } + // cross-profile access is always allowed here to allow profile-switching. if (!mSettings.isCurrentProfile(userId)) { Slog.w(TAG, "A background user is requesting window. Hiding IME.");