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

Commit 90622cfc authored by Yohei Yukawa's avatar Yohei Yukawa
Browse files

Introduce per-user InputMethodSettings cache

As a preparation for concurrent multi-user support in
InputMethodManagerService, this CL introduces

  InputMethodSettingsRepository

as a per-user InputMethodSettings repository.

Note that InputMethodSettingsRepository.get(userId) has not yet used
in this CL thus there must be no user observable behavior change.

Bug: 309837937
Test: presubmit
Change-Id: Iccede363caffb04087f511b6585cf07565dafda1
parent 4d88d1b1
Loading
Loading
Loading
Loading
+13 −3
Original line number Diff line number Diff line
@@ -19,11 +19,13 @@ package com.android.server.inputmethod;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Handler;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;

@@ -67,7 +69,7 @@ final class AdditionalSubtypeMapRepository {
        AdditionalSubtypeUtils.save(map, inputMethodMap, userId);
    }

    static void initialize(@NonNull Handler handler) {
    static void initialize(@NonNull Handler handler, @NonNull Context context) {
        final UserManagerInternal userManagerInternal =
                LocalServices.getService(UserManagerInternal.class);
        handler.post(() -> {
@@ -79,8 +81,16 @@ final class AdditionalSubtypeMapRepository {
                            handler.post(() -> {
                                synchronized (ImfLock.class) {
                                    if (!sPerUserMap.contains(userId)) {
                                        sPerUserMap.put(userId,
                                                AdditionalSubtypeUtils.load(userId));
                                        final AdditionalSubtypeMap additionalSubtypeMap =
                                                AdditionalSubtypeUtils.load(userId);
                                        sPerUserMap.put(userId, additionalSubtypeMap);
                                        final InputMethodSettings settings =
                                                InputMethodManagerService
                                                        .queryInputMethodServicesInternal(context,
                                                                userId,
                                                                additionalSubtypeMap,
                                                                DirectBootAwareness.AUTO);
                                        InputMethodSettingsRepository.put(userId, settings);
                                    }
                                }
                            });
+46 −31
Original line number Diff line number Diff line
@@ -1208,9 +1208,17 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
            if (!mSystemReady) {
                return;
            }
            mSettings = queryInputMethodServicesInternal(mContext, mSettings.getUserId(),
                    AdditionalSubtypeMapRepository.get(mSettings.getUserId()),
            for (int userId : mUserManagerInternal.getUserIds()) {
                final InputMethodSettings settings = queryInputMethodServicesInternal(
                                mContext,
                                userId,
                                AdditionalSubtypeMapRepository.get(userId),
                                DirectBootAwareness.AUTO);
                InputMethodSettingsRepository.put(userId, settings);
                if (userId == mSettings.getUserId()) {
                    mSettings = settings;
                }
            }
            postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */);
            // If the locale is changed, needs to reset the default ime
            resetDefaultImeLocked(mContext);
@@ -1457,12 +1465,15 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
                    AdditionalSubtypeMapRepository.putAndSave(userId, newAdditionalSubtypeMap,
                            settings.getMethodMap());
                }

                if (!isCurrentUser) {
                if (isCurrentUser
                        && !(additionalSubtypeChanged || shouldRebuildInputMethodListLocked())) {
                    return;
                }

                if (!(additionalSubtypeChanged || shouldRebuildInputMethodListLocked())) {
                final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
                        userId, newAdditionalSubtypeMap, DirectBootAwareness.AUTO);
                InputMethodSettingsRepository.put(userId, newSettings);
                if (!isCurrentUser) {
                    return;
                }
                mSettings = queryInputMethodServicesInternal(mContext, userId,
@@ -1619,23 +1630,24 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

    void onUnlockUser(@UserIdInt int userId) {
        synchronized (ImfLock.class) {
            final int currentUserId = mSettings.getUserId();
            if (DEBUG) {
                Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId=" + currentUserId);
            }
            if (userId != currentUserId) {
                return;
                Slog.d(TAG, "onUnlockUser: userId=" + userId + " curUserId="
                        + mSettings.getUserId());
            }
            if (!mSystemReady) {
                return;
            }
            mSettings = queryInputMethodServicesInternal(mContext, userId,
                    AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO);
            final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
                    userId, AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO);
            InputMethodSettingsRepository.put(userId, newSettings);
            if (mSettings.getUserId() == userId) {
                mSettings = newSettings;
                // We need to rebuild IMEs.
                postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
                updateInputMethodsFromSettingsLocked(true /* enabledChanged */);
            }
        }
    }

    @GuardedBy("ImfLock.class")
    void scheduleSwitchUserTaskLocked(@UserIdInt int userId,
@@ -1697,12 +1709,13 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

        mShowOngoingImeSwitcherForPhones = false;

        AdditionalSubtypeMapRepository.initialize(mHandler);
        // InputMethodSettingsRepository should be initialized before buildInputMethodListLocked
        InputMethodSettingsRepository.initialize(mHandler, mContext);
        AdditionalSubtypeMapRepository.initialize(mHandler, mContext);

        final int userId = mActivityManagerInternal.getCurrentUserId();

        // mSettings should be created before buildInputMethodListLocked
        mSettings = InputMethodSettings.createEmptyMap(userId);
        mSettings = InputMethodSettingsRepository.get(userId);

        mSwitchingController =
                InputMethodSubtypeSwitchingController.createInstanceLocked(context,
@@ -1866,8 +1879,10 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub
        // and user switch would not happen at that time.
        resetCurrentMethodAndClientLocked(UnbindReason.SWITCH_USER);

        mSettings = queryInputMethodServicesInternal(mContext, newUserId,
                AdditionalSubtypeMapRepository.get(newUserId), DirectBootAwareness.AUTO);
        final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
                newUserId, AdditionalSubtypeMapRepository.get(newUserId), DirectBootAwareness.AUTO);
        InputMethodSettingsRepository.put(newUserId, newSettings);
        mSettings = newSettings;
        postInputMethodSettingUpdatedLocked(initialUserSwitch /* resetDefaultEnabledIme */);
        if (TextUtils.isEmpty(mSettings.getSelectedInputMethod())) {
            // This is the first time of the user switch and
@@ -1949,9 +1964,11 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

                final String defaultImiId = mSettings.getSelectedInputMethod();
                final boolean imeSelectedOnBoot = !TextUtils.isEmpty(defaultImiId);
                mSettings = queryInputMethodServicesInternal(mContext, currentUserId,
                        AdditionalSubtypeMapRepository.get(mSettings.getUserId()),
                final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
                        currentUserId, AdditionalSubtypeMapRepository.get(currentUserId),
                        DirectBootAwareness.AUTO);
                InputMethodSettingsRepository.put(currentUserId, newSettings);
                mSettings = newSettings;
                postInputMethodSettingUpdatedLocked(
                        !imeSelectedOnBoot /* resetDefaultEnabledIme */);
                updateFromSettingsLocked(true);
@@ -4386,22 +4403,20 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub

            final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
            final boolean isCurrentUser = (mSettings.getUserId() == userId);
            final InputMethodSettings settings = isCurrentUser
                    ? mSettings
                    : queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
                            DirectBootAwareness.AUTO);
            final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);
            final var newAdditionalSubtypeMap = settings.getNewAdditionalSubtypeMap(
                    imiId, toBeAdded, additionalSubtypeMap, mPackageManagerInternal, callingUid);
            if (additionalSubtypeMap != newAdditionalSubtypeMap) {
                AdditionalSubtypeMapRepository.putAndSave(userId, newAdditionalSubtypeMap,
                        settings.getMethodMap());
                final InputMethodSettings newSettings = queryInputMethodServicesInternal(mContext,
                        userId, AdditionalSubtypeMapRepository.get(userId),
                        DirectBootAwareness.AUTO);
                InputMethodSettingsRepository.put(userId, newSettings);
                if (isCurrentUser) {
                    final long ident = Binder.clearCallingIdentity();
                    try {
                        mSettings = queryInputMethodServicesInternal(mContext,
                                mSettings.getUserId(),
                                AdditionalSubtypeMapRepository.get(mSettings.getUserId()),
                                DirectBootAwareness.AUTO);
                        mSettings = newSettings;
                        postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */);
                    } finally {
                        Binder.restoreCallingIdentity(ident);
+86 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 android.annotation.NonNull;
import android.annotation.UserIdInt;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Handler;
import android.util.SparseArray;

import com.android.internal.annotations.GuardedBy;
import com.android.internal.inputmethod.DirectBootAwareness;
import com.android.server.LocalServices;
import com.android.server.pm.UserManagerInternal;

final class InputMethodSettingsRepository {
    @GuardedBy("ImfLock.class")
    @NonNull
    private static final SparseArray<InputMethodSettings> sPerUserMap = new SparseArray<>();

    /**
     * Not intended to be instantiated.
     */
    private InputMethodSettingsRepository() {
    }

    @NonNull
    @GuardedBy("ImfLock.class")
    static InputMethodSettings get(@UserIdInt int userId) {
        final InputMethodSettings obj = sPerUserMap.get(userId);
        if (obj != null) {
            return obj;
        }
        return InputMethodSettings.createEmptyMap(userId);
    }

    @GuardedBy("ImfLock.class")
    static void put(@UserIdInt int userId, @NonNull InputMethodSettings obj) {
        sPerUserMap.put(userId, obj);
    }

    static void initialize(@NonNull Handler handler, @NonNull Context context) {
        final UserManagerInternal userManagerInternal =
                LocalServices.getService(UserManagerInternal.class);
        handler.post(() -> {
            userManagerInternal.addUserLifecycleListener(
                    new UserManagerInternal.UserLifecycleListener() {
                        @Override
                        public void onUserRemoved(UserInfo user) {
                            final int userId = user.id;
                            handler.post(() -> {
                                synchronized (ImfLock.class) {
                                    sPerUserMap.remove(userId);
                                }
                            });
                        }
                    });
            synchronized (ImfLock.class) {
                for (int userId : userManagerInternal.getUserIds()) {
                    final InputMethodSettings settings =
                            InputMethodManagerService.queryInputMethodServicesInternal(
                                    context,
                                    userId,
                                    AdditionalSubtypeMapRepository.get(userId),
                                    DirectBootAwareness.AUTO);
                    sPerUserMap.put(userId, settings);
                }
            }
        });
    }
}