From fd8f72188cb409165aa7a810d469fc471a15e207 Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Tue, 22 Jan 2019 19:25:44 -0800 Subject: [PATCH] Client-side reimplementation of IMM#setCurrentInputMethodSubtype() This is a preparation to mark InputMethodManager#setCurrentInputMethodSubtype() deprecated. InputMethodManager#setCurrentInputMethodSubtype(), which was introduced in Android 4.0 ICS [1], was probably mistakenly exposed as a public API, because it has required WRITE_SECURE_SETTINGS that typical applications cannot have. Keeping maintaining InputMethodManager#setCurrentInputMethodSubtype() is not that simple because now we are about to enable per-profile IME mode, where this method needs to have a more clear spec about what "Current" means. An ideal solution is just removing this method, because if the caller already has WRITE_SECURE_SETTINGS permission, they can just directly update Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE to achieve the same goal. However, given that this has been a public API, it would probably make sense to provide a fallback implementation like I did for null IME token in InputMethodManager#setInputMethod() [2]. Either way, InputMethodManager#setCurrentInputMethodSubtype() will be marked as deprecated in a subsequent CL. [1]: I55daa19ba924999def544bf841f00bf54852f3e1 b66d287e3003a0934d5714fbf15e554b3c814906 [2]: I42dd0325b01c527009bf85566ca8ba0766b2294e 0c1ebffdb3a459dbae549d462b97cdcdc5602816 Bug: 123249820 Test: manually done with a test app that has WRITE_SECURE_SETTINGS Change-Id: I76da83c57cffc6b73defccfd4a1b5734c958a97e --- .../view/inputmethod/InputMethodManager.java | 46 +++++++++++++++++-- .../internal/view/IInputMethodManager.aidl | 1 - .../InputMethodManagerService.java | 19 -------- .../MultiClientInputMethodManagerService.java | 7 --- 4 files changed, 42 insertions(+), 31 deletions(-) diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 14a5e3bd1417a..15088d2070d23 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -27,6 +27,7 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.app.ActivityThread; +import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; @@ -2492,11 +2493,48 @@ public final class InputMethodManager { */ @RequiresPermission(WRITE_SECURE_SETTINGS) public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { - try { - return mService.setCurrentInputMethodSubtype(subtype); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + if (Process.myUid() == Process.SYSTEM_UID) { + Log.w(TAG, "System process should not call setCurrentInputMethodSubtype() because " + + "almost always it is a bug under multi-user / multi-profile environment. " + + "Consider directly interacting with InputMethodManagerService " + + "via LocalServices."); + return false; } + if (subtype == null) { + // See the JavaDoc. This is how this method has worked. + return false; + } + final Context fallbackContext = ActivityThread.currentApplication(); + if (fallbackContext == null) { + return false; + } + if (fallbackContext.checkSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + return false; + } + final ContentResolver contentResolver = fallbackContext.getContentResolver(); + final String imeId = Settings.Secure.getString(contentResolver, + Settings.Secure.DEFAULT_INPUT_METHOD); + if (ComponentName.unflattenFromString(imeId) == null) { + // Null or invalid IME ID format. + return false; + } + final List enabledSubtypes; + try { + enabledSubtypes = mService.getEnabledInputMethodSubtypeList(imeId, true); + } catch (RemoteException e) { + return false; + } + final int numSubtypes = enabledSubtypes.size(); + for (int i = 0; i < numSubtypes; ++i) { + final InputMethodSubtype enabledSubtype = enabledSubtypes.get(i); + if (enabledSubtype.equals(subtype)) { + Settings.Secure.putInt(contentResolver, + Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, enabledSubtype.hashCode()); + return true; + } + } + return false; } /** diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 8194a920d3319..0752efe562a96 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -64,7 +64,6 @@ interface IInputMethodManager { void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId); boolean isInputMethodPickerShownForTest(); InputMethodSubtype getCurrentInputMethodSubtype(); - boolean setCurrentInputMethodSubtype(in InputMethodSubtype subtype); void setAdditionalInputMethodSubtypes(String id, in InputMethodSubtype[] subtypes); // This is kept due to @UnsupportedAppUsage. // TODO(Bug 113914148): Consider removing this. diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index eca371a78750a..3fd3945fc87df 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -4106,25 +4106,6 @@ public class InputMethodManagerService extends IInputMethodManager.Stub return mCurrentSubtype; } - @Override - public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { - synchronized (mMethodMap) { - // TODO: Make this work even for non-current users? - if (!calledFromValidUserLocked()) { - return false; - } - if (subtype != null && mCurMethodId != null) { - InputMethodInfo imi = mMethodMap.get(mCurMethodId); - int subtypeId = InputMethodUtils.getSubtypeIdFromHashCode(imi, subtype.hashCode()); - if (subtypeId != NOT_A_SUBTYPE_ID) { - setInputMethodLocked(mCurMethodId, subtypeId); - return true; - } - } - return false; - } - } - private List getInputMethodListAsUser(@UserIdInt int userId) { synchronized (mMethodMap) { return getInputMethodListLocked(userId); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index a31b3b4b3b5aa..3222143fc89f5 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1517,13 +1517,6 @@ public final class MultiClientInputMethodManagerService { return null; } - @BinderThread - @Override - public boolean setCurrentInputMethodSubtype(InputMethodSubtype subtype) { - reportNotSupported(); - return false; - } - @BinderThread @Override public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes) {