From 0c1ebffdb3a459dbae549d462b97cdcdc5602816 Mon Sep 17 00:00:00 2001 From: Yohei Yukawa Date: Thu, 27 Dec 2018 14:06:28 -0800 Subject: [PATCH] Deprecate null IME token rule in IMM#setInputMethod() With my previous CL [1], InputMethodManagerService#setInputMethod() is now guaranteed to be called only from IInputMethodManager and IInputMethodPrivilegedOperations as 'adb shell ime set' no longer directly calls this method (with null IME token). With this CL, IInputMethodManager#setInputMethod(), which has been kept just for null IME token rule, is finally gone. This is achieved by letting InputMethodManager#setInputMethod() directly update DEFAULT_INPUT_METHOD (and SELECTED_INPUT_METHOD_SUBTYPE) secure settings if a priviledged component still relies on this undocumented null IME token rule. [1]: I6fd47b5cc1e7da7222774df20247a2c69a70f45b db25df71be4c1bcc654f69ce9a8bff7e3ef46360 Fix: 114488811 Test: atest CtsInputMethodServiceHostTestCases Change-Id: I42dd0325b01c527009bf85566ca8ba0766b2294e --- .../view/inputmethod/InputMethodManager.java | 63 ++++++++++++++++--- .../internal/view/IInputMethodManager.aidl | 2 - .../InputMethodManagerService.java | 6 +- .../MultiClientInputMethodManagerService.java | 6 -- 4 files changed, 59 insertions(+), 18 deletions(-) diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 36e81e6187c94..1ba7d8ed5db23 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -26,6 +26,8 @@ import android.annotation.RequiresPermission; import android.annotation.SystemService; import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; +import android.app.ActivityThread; +import android.content.ContentResolver; import android.content.Context; import android.content.pm.PackageManager; import android.graphics.Rect; @@ -37,11 +39,13 @@ import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; +import android.os.Process; import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ServiceManager; import android.os.ServiceManager.ServiceNotFoundException; import android.os.Trace; +import android.provider.Settings; import android.text.style.SuggestionSpan; import android.util.Log; import android.util.Pools.Pool; @@ -232,6 +236,8 @@ public final class InputMethodManager { static final String PENDING_EVENT_COUNTER = "aq:imm"; + private static final int NOT_A_SUBTYPE_ID = -1; + /** * A constant that represents Voice IME. * @@ -2071,6 +2077,13 @@ public final class InputMethodManager { /** * Force switch to a new input method component. This can only be called * from an application or a service which has a token of the currently active input method. + * + *

On Android {@link Build.VERSION_CODES#Q} and later devices, the undocumented behavior that + * token can be {@code null} when the caller has + * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} is deprecated. Instead, update + * {@link android.provider.Settings.Secure#DEFAULT_INPUT_METHOD} and + * {@link android.provider.Settings.Secure#SELECTED_INPUT_METHOD_SUBTYPE} directly.

+ * * @param token Supplies the identifying token given to an input method * when it was started, which allows it to perform this operation on * itself. @@ -2082,14 +2095,50 @@ public final class InputMethodManager { @Deprecated public void setInputMethod(IBinder token, String id) { if (token == null) { - // Note: null token is allowed for callers that have WRITE_SECURE_SETTINGS permission. - // Thus we cannot always rely on InputMethodPrivilegedOperationsRegistry unfortunately. - // TODO(Bug 114488811): Consider deprecating null token rule. - try { - mService.setInputMethod(token, id); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + // There are still some system components that rely on this undocumented behavior + // regarding null IME token with WRITE_SECURE_SETTINGS. Provide a fallback logic as a + // temporary remedy. + if (id == null) { + return; } + if (Process.myUid() == Process.SYSTEM_UID) { + Log.w(TAG, "System process should not be calling setInputMethod() because almost " + + "always it is a bug under multi-user / multi-profile environment. " + + "Consider interacting with InputMethodManagerService directly via " + + "LocalServices."); + return; + } + final Context fallbackContext = ActivityThread.currentApplication(); + if (fallbackContext == null) { + return; + } + if (fallbackContext.checkSelfPermission(WRITE_SECURE_SETTINGS) + != PackageManager.PERMISSION_GRANTED) { + return; + } + final List imis = getEnabledInputMethodList(); + final int numImis = imis.size(); + boolean found = false; + for (int i = 0; i < numImis; ++i) { + final InputMethodInfo imi = imis.get(i); + if (id.equals(imi.getId())) { + found = true; + break; + } + } + if (!found) { + Log.e(TAG, "Ignoring setInputMethod(null, " + id + ") because the specified " + + "id not found in enabled IMEs."); + return; + } + Log.w(TAG, "The undocumented behavior that setInputMethod() accepts null token " + + "when the caller has WRITE_SECURE_SETTINGS is deprecated. This behavior may " + + "be completely removed in a future version. Update secure settings directly " + + "instead."); + final ContentResolver resolver = fallbackContext.getContentResolver(); + Settings.Secure.putInt(resolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, + NOT_A_SUBTYPE_ID); + Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD, id); return; } InputMethodPrivilegedOperationsRegistry.get(token).setInputMethod(id); diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl index 3873d2450738e..70f4ed2d529e9 100644 --- a/core/java/com/android/internal/view/IInputMethodManager.aidl +++ b/core/java/com/android/internal/view/IInputMethodManager.aidl @@ -65,8 +65,6 @@ interface IInputMethodManager { int displayId); void showInputMethodAndSubtypeEnablerFromClient(in IInputMethodClient client, String topId); boolean isInputMethodPickerShownForTest(); - // TODO(Bug 114488811): this can be removed once we deprecate special null token rule. - void setInputMethod(in IBinder token, String id); void registerSuggestionSpansForNotification(in SuggestionSpan[] spans); boolean notifySuggestionPicked(in SuggestionSpan span, String originalString, int index); InputMethodSubtype getCurrentInputMethodSubtype(); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index ef7555b5d8be7..ad0886145496a 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -3078,10 +3078,10 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } - @Override - public void setInputMethod(IBinder token, String id) { + @BinderThread + private void setInputMethod(IBinder token, String id) { synchronized (mMethodMap) { - if (!calledFromValidUserLocked()) { + if (!calledWithValidTokenLocked(token)) { return; } setInputMethodWithSubtypeIdLocked(token, id, NOT_A_SUBTYPE_ID); diff --git a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java index ffe14d9f2877f..98ed3ea92fa1c 100644 --- a/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/MultiClientInputMethodManagerService.java @@ -1511,12 +1511,6 @@ public final class MultiClientInputMethodManagerService { return false; } - @BinderThread - @Override - public void setInputMethod(IBinder token, String id) { - reportNotSupported(); - } - @BinderThread @Override public void registerSuggestionSpansForNotification(SuggestionSpan[] suggestionSpans) {