Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0c1ebffd authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

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
      db25df71

Fix: 114488811
Test: atest CtsInputMethodServiceHostTestCases
Change-Id: I42dd0325b01c527009bf85566ca8ba0766b2294e
parent 006892ff
Loading
Loading
Loading
Loading
+56 −7
Original line number Diff line number Diff line
@@ -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.
     *
     * <p>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.</p>
     *
     * @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<InputMethodInfo> 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);
+0 −2
Original line number Diff line number Diff line
@@ -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();
+3 −3
Original line number Diff line number Diff line
@@ -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);
+0 −6
Original line number Diff line number Diff line
@@ -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) {