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

Commit afec9a24 authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Use the right user's spell checker in InputMethodUtils

This CL fixes a regression that was introduced when
TextServicesManagerService (TSMS) was updated to support multiple
users [1].

Now TextServicesManager#getCurrentSpellChecker() always returns a
result for the calling user, if it is called from the system server
process it always returns the result for the main user (user ID = 0),
unless the caller is in the middle of IPC transaction.

InputMethodUtils#setNonSelectedSystemImesDisabledUntilUsed() had this
issue.  To make it correctly work, we need a special version of
getCurrentSpellChecker() that takes user ID as a parameter.

Luckily InputMethodUtils is guaranteed to be running in the system
server process [2], we can simply rely on TextServicesManagerInternal
instead of adding @hide APIs into TextServicesManager.

 [1]: I06c27ef834203a21cc445dc126602c799384527b
      06a26240
 [2]: I37594096151d77adb6741a41d7985a4506f96ccb
      e6b6e0e7

Bug: 63041121
Test: manually done
Change-Id: I278fce6d8c588aaa5a12936fcab45be29a6c3a0b
parent 75504b17
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -38,11 +38,11 @@ import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.InputMethodSubtype;
import android.view.textservice.SpellCheckerInfo;
import android.view.textservice.TextServicesManager;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.server.textservices.TextServicesManagerInternal;

import java.util.ArrayList;
import java.util.Arrays;
@@ -641,7 +641,7 @@ final class InputMethodUtils {
        }
        // Only the current spell checker should be treated as an enabled one.
        final SpellCheckerInfo currentSpellChecker =
                TextServicesManager.getInstance().getCurrentSpellChecker();
                TextServicesManagerInternal.get().getCurrentSpellCheckerForUser(userId);
        for (final String packageName : systemImesDisabledUntilUsed) {
            if (DEBUG) {
                Slog.d(TAG, "check " + packageName);
+63 −0
Original line number Diff line number Diff line
/*
 * 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.textservices;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.view.textservice.SpellCheckerInfo;

import com.android.server.LocalServices;

/**
 * Local interface of {@link TextServicesManagerService} inside system server process.
 */
public abstract class TextServicesManagerInternal {
    /**
     * Returns the list of installed input methods for the specified user.
     *
     * <p>CAVEAT: This method is not fully implemented yet. This may return an empty list if
     * {@code userId} for a background user is specified. Check the implementation before starting
     * this method.</p>
     *
     * @param userId The user ID to be queried.
     * @return {@link SpellCheckerInfo} that is currently selected {@code userId}.
     */
    @Nullable
    public abstract SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId);

    /**
     * Fake implementation of {@link TextServicesManagerInternal}.  All the methods do nothing.
     */
    private static final TextServicesManagerInternal NOP =
            new TextServicesManagerInternal() {
                @Override
                public SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) {
                    return null;
                }
            };

    /**
     * @return Global instance if exists.  Otherwise, a dummy no-op instance.
     */
    @NonNull
    public static TextServicesManagerInternal get() {
        final TextServicesManagerInternal instance =
                LocalServices.getService(TextServicesManagerInternal.class);
        return instance != null ? instance : NOP;
    }
}
+18 −0
Original line number Diff line number Diff line
@@ -56,6 +56,7 @@ import com.android.internal.textservice.ISpellCheckerSessionListener;
import com.android.internal.textservice.ITextServicesManager;
import com.android.internal.textservice.ITextServicesSessionListener;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;

import org.xmlpull.v1.XmlPullParserException;
@@ -278,6 +279,14 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {

        @Override
        public void onStart() {
            LocalServices.addService(TextServicesManagerInternal.class,
                    new TextServicesManagerInternal() {
                        @Override
                        public SpellCheckerInfo getCurrentSpellCheckerForUser(
                                @UserIdInt int userId) {
                            return mService.getCurrentSpellCheckerForUser(userId);
                        }
                    });
            publishBinderService(Context.TEXT_SERVICES_MANAGER_SERVICE, mService);
        }

@@ -493,6 +502,15 @@ public class TextServicesManagerService extends ITextServicesManager.Stub {
        return spellCheckerList.get(0);
    }

    @Nullable
    private SpellCheckerInfo getCurrentSpellCheckerForUser(@UserIdInt int userId) {
        synchronized (mLock) {
            final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(userId);
            final TextServicesData data = mUserData.get(spellCheckerOwnerUserId);
            return data != null ? data.getCurrentSpellChecker() : null;
        }
    }

    // TODO: Save SpellCheckerService by supported languages. Currently only one spell
    // checker is saved.
    @Override