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

Commit 0c88439c authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Minimize invocations of queryIntentServices() in IMMS" into main

parents 04d6a542 91a851e4
Loading
Loading
Loading
Loading
+56 −66
Original line number Diff line number Diff line
@@ -717,13 +717,19 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                return;
            }
            for (int userId : mUserManagerInternal.getUserIds()) {
                final InputMethodSettings settings = queryInputMethodServicesInternal(
                        mContext,
                        userId,
                        AdditionalSubtypeMapRepository.get(userId),
                        DirectBootAwareness.AUTO);
                // Does InputMethodInfo really have data dependency on system locale?
                // TODO(b/356679261): Check if we really need to update RawInputMethodInfo here.
                {
                    final var userData = getUserData(userId);
                    final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
                    final var rawMethodMap = queryRawInputMethodServiceMap(mContext, userId);
                    userData.mRawInputMethodMap.set(rawMethodMap);
                    final var methodMap = rawMethodMap.toInputMethodMap(additionalSubtypeMap,
                            DirectBootAwareness.AUTO,
                            mUserManagerInternal.isUserUnlockingOrUnlocked(userId));
                    final var settings = InputMethodSettings.create(methodMap, userId);
                    InputMethodSettingsRepository.put(userId, settings);

                }
                postInputMethodSettingUpdatedLocked(true /* resetDefaultEnabledIme */, userId);
                // If the locale is changed, needs to reset the default ime
                resetDefaultImeLocked(mContext, userId);
@@ -797,17 +803,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.

        private void onFinishPackageChangesInternal() {
            final int userId = getChangingUserId();
            final var userData = getUserData(userId);

            // Instantiating InputMethodInfo requires disk I/O.
            // Do them before acquiring the lock to minimize the chances of ANR (b/340221861).
            final var newMethodMapWithoutAdditionalSubtypes =
                    queryInputMethodServicesInternal(mContext, userId,
                            AdditionalSubtypeMap.EMPTY_MAP, DirectBootAwareness.AUTO)
                            .getMethodMap();
            userData.mRawInputMethodMap.set(queryRawInputMethodServiceMap(mContext, userId));

            synchronized (ImfLock.class) {
                final AdditionalSubtypeMap additionalSubtypeMap =
                        AdditionalSubtypeMapRepository.get(userId);
                final InputMethodSettings settings = InputMethodSettingsRepository.get(userId);

                InputMethodInfo curIm = null;
@@ -837,6 +839,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                }

                // Clear additional subtypes as a batch operation.
                final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
                final AdditionalSubtypeMap newAdditionalSubtypeMap =
                        additionalSubtypeMap.cloneWithRemoveOrSelf(imesToClearAdditionalSubtypes);
                final boolean additionalSubtypeChanged =
@@ -846,8 +849,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                            settings.getMethodMap());
                }

                final var newMethodMap = newMethodMapWithoutAdditionalSubtypes
                        .applyAdditionalSubtypes(newAdditionalSubtypeMap);
                final var newMethodMap = userData.mRawInputMethodMap.get().toInputMethodMap(
                        newAdditionalSubtypeMap,
                        DirectBootAwareness.AUTO,
                        mUserManagerInternal.isUserUnlockingOrUnlocked(userId));

                if (InputMethodMap.areSame(settings.getMethodMap(), newMethodMap)) {
                    // No update in the actual IME map.
@@ -1065,9 +1070,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
            final int userId = user.getUserIdentifier();
            SecureSettingsWrapper.onUserUnlocking(userId);
            mService.mIoHandler.post(() -> {
                final var settings = queryInputMethodServicesInternal(mService.mContext, userId,
                        AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO);
                InputMethodSettingsRepository.put(userId, settings);
                final var userData = mService.getUserData(userId);
                final var methodMap = userData.mRawInputMethodMap.get().toInputMethodMap(
                        AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO, true);
                final var newSettings = InputMethodSettings.create(methodMap, userId);
                InputMethodSettingsRepository.put(userId, newSettings);
                synchronized (ImfLock.class) {
                    if (!mService.mSystemReady) {
                        return;
@@ -1105,19 +1112,22 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.

                for (int userId : userIds) {
                    Slog.d(TAG, "Start initialization for user=" + userId);
                    final var userData = mService.getUserData(userId);

                    AdditionalSubtypeMapRepository.initializeIfNecessary(userId);
                    final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
                    final var settings = InputMethodManagerService.queryInputMethodServicesInternal(
                            context, userId, additionalSubtypeMap,
                            DirectBootAwareness.AUTO).getMethodMap();
                    InputMethodSettingsRepository.put(userId,
                            InputMethodSettings.create(settings, userId));
                    final var rawMethodMap = queryRawInputMethodServiceMap(context, userId);
                    userData.mRawInputMethodMap.set(rawMethodMap);
                    final var methodMap = rawMethodMap.toInputMethodMap(additionalSubtypeMap,
                            DirectBootAwareness.AUTO,
                            userManagerInternal.isUserUnlockingOrUnlocked(userId));
                    final var settings = InputMethodSettings.create(methodMap, userId);
                    InputMethodSettingsRepository.put(userId, settings);

                    final int profileParentId = userManagerInternal.getProfileParentId(userId);
                    final boolean value =
                            InputMethodDrawsNavBarResourceMonitor.evaluate(context,
                                    profileParentId);
                    final var userData = mService.getUserData(userId);
                    userData.mImeDrawsNavBar.set(value);

                    userData.mBackgroundLoadLatch.countDown();
@@ -1132,12 +1142,13 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
            // Called on ActivityManager thread.
            SecureSettingsWrapper.onUserStopped(userId);
            mService.mIoHandler.post(() -> {
                final var userData = mService.getUserData(userId);
                final var additionalSubtypeMap = AdditionalSubtypeMapRepository.get(userId);
                final var settings = InputMethodManagerService.queryInputMethodServicesInternal(
                        mService.mContext, userId, additionalSubtypeMap,
                        DirectBootAwareness.AUTO).getMethodMap();
                final var rawMethodMap = userData.mRawInputMethodMap.get();
                final var methodMap = rawMethodMap.toInputMethodMap(additionalSubtypeMap,
                        DirectBootAwareness.AUTO, false /* userUnlocked */);
                InputMethodSettingsRepository.put(userId,
                        InputMethodSettings.create(settings, userId));
                        InputMethodSettings.create(methodMap, userId));
            });
        }
    }
@@ -1635,15 +1646,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.

    private List<InputMethodInfo> getInputMethodListInternal(@UserIdInt int userId,
            @DirectBootAwareness int directBootAwareness, int callingUid) {
        final InputMethodSettings settings;
        if (directBootAwareness == DirectBootAwareness.AUTO) {
            settings = InputMethodSettingsRepository.get(userId);
        } else {
            final AdditionalSubtypeMap additionalSubtypeMap =
                    AdditionalSubtypeMapRepository.get(userId);
            settings = queryInputMethodServicesInternal(mContext, userId, additionalSubtypeMap,
                    directBootAwareness);
        }
        final var userData = getUserData(userId);
        final var methodMap = userData.mRawInputMethodMap.get().toInputMethodMap(
                AdditionalSubtypeMapRepository.get(userId), directBootAwareness,
                mUserManagerInternal.isUserUnlockingOrUnlocked(userId));
        final var settings = InputMethodSettings.create(methodMap, userId);
        // Create a copy.
        final ArrayList<InputMethodInfo> methodList = new ArrayList<>(settings.getMethodList());
        // filter caller's access to input methods
@@ -4342,6 +4349,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                        + subtype.getLocale() + ", " + subtype.getMode());
            }
        }
        final var userData = getUserData(userId);
        synchronized (ImfLock.class) {
            if (!mSystemReady) {
                return;
@@ -4357,9 +4365,10 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                        settings.getMethodMap());
                final long ident = Binder.clearCallingIdentity();
                try {
                    final InputMethodSettings newSettings = queryInputMethodServicesInternal(
                            mContext, userId, AdditionalSubtypeMapRepository.get(userId),
                            DirectBootAwareness.AUTO);
                    final var methodMap = userData.mRawInputMethodMap.get().toInputMethodMap(
                            AdditionalSubtypeMapRepository.get(userId), DirectBootAwareness.AUTO,
                            mUserManagerInternal.isUserUnlockingOrUnlocked(userId));
                    final var newSettings = InputMethodSettings.create(methodMap, userId);
                    InputMethodSettingsRepository.put(userId, newSettings);
                    if (isCurrentUser) {
                        postInputMethodSettingUpdatedLocked(false /* resetDefaultEnabledIme */,
@@ -5298,31 +5307,15 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
    }

    @NonNull
    static InputMethodSettings queryInputMethodServicesInternal(Context context,
            @UserIdInt int userId, @NonNull AdditionalSubtypeMap additionalSubtypeMap,
            @DirectBootAwareness int directBootAwareness) {
    static RawInputMethodMap queryRawInputMethodServiceMap(Context context, @UserIdInt int userId) {
        final Context userAwareContext = context.getUserId() == userId
                ? context
                : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */);

        final int directBootAwarenessFlags;
        switch (directBootAwareness) {
            case DirectBootAwareness.ANY:
                directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AWARE
                        | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
                break;
            case DirectBootAwareness.AUTO:
                directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AUTO;
                break;
            default:
                directBootAwarenessFlags = PackageManager.MATCH_DIRECT_BOOT_AUTO;
                Slog.e(TAG, "Unknown directBootAwareness=" + directBootAwareness
                        + ". Falling back to DirectBootAwareness.AUTO");
                break;
        }
        final int flags = PackageManager.GET_META_DATA
                | PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS
                | directBootAwarenessFlags;
                | PackageManager.MATCH_DIRECT_BOOT_AWARE
                | PackageManager.MATCH_DIRECT_BOOT_UNAWARE;

        // Beware that package visibility filtering will be enforced based on the effective calling
        // identity (Binder.getCallingUid()), but our use case always expect Binder.getCallingUid()
@@ -5338,14 +5331,11 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
        final List<String> enabledInputMethodList =
                InputMethodUtils.getEnabledInputMethodIdsForFiltering(context, userId);

        final InputMethodMap methodMap = filterInputMethodServices(
                additionalSubtypeMap, enabledInputMethodList, userAwareContext, services);
        return InputMethodSettings.create(methodMap, userId);
        return filterInputMethodServices(enabledInputMethodList, userAwareContext, services);
    }

    @NonNull
    static InputMethodMap filterInputMethodServices(
            @NonNull AdditionalSubtypeMap additionalSubtypeMap,
    static RawInputMethodMap filterInputMethodServices(
            List<String> enabledInputMethodList, Context userAwareContext,
            List<ResolveInfo> services) {
        final ArrayMap<String, Integer> imiPackageCount = new ArrayMap<>();
@@ -5366,7 +5356,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.

            try {
                final InputMethodInfo imi = new InputMethodInfo(userAwareContext, ri,
                        additionalSubtypeMap.get(imeId));
                        Collections.emptyList());
                if (imi.isVrOnly()) {
                    continue;  // Skip VR-only IME, which isn't supported for now.
                }
@@ -5389,7 +5379,7 @@ public final class InputMethodManagerService implements IInputMethodManagerImpl.
                Slog.wtf(TAG, "Unable to load input method " + imeId, e);
            }
        }
        return InputMethodMap.of(methodMap);
        return RawInputMethodMap.of(methodMap);
    }

    @GuardedBy("ImfLock.class")
+105 −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.AnyThread;
import android.annotation.NonNull;
import android.util.ArrayMap;
import android.util.Slog;
import android.view.inputmethod.InputMethodInfo;

import com.android.internal.inputmethod.DirectBootAwareness;

import java.util.List;

/**
 * This is quite similar to {@link InputMethodMap} with two major differences.
 *
 * <ul>
 *     <li>Additional {@link android.view.inputmethod.InputMethodSubtype} is not included.</li>
 *     <li>Always include direct-boot unaware {@link android.inputmethodservice.InputMethodService}.
 *     </li>
 * </ul>
 *
 * <p>As seen in {@link #toInputMethodMap(AdditionalSubtypeMap, int, boolean)}, you can consider
 * this is a prototype data where you can always derive {@link InputMethodMap} with
 * {@link AdditionalSubtypeMap} and a boolean information whether
 * {@link com.android.server.pm.UserManagerInternal#isUserUnlockingOrUnlocked(int)} returns
 * {@code true} or not.</p>
 */
final class RawInputMethodMap {
    static final String TAG = "RawInputMethodMap";

    private static final ArrayMap<String, InputMethodInfo> EMPTY_MAP = new ArrayMap<>();

    private final ArrayMap<String, InputMethodInfo> mMap;

    static RawInputMethodMap emptyMap() {
        return new RawInputMethodMap(EMPTY_MAP);
    }

    static RawInputMethodMap of(@NonNull ArrayMap<String, InputMethodInfo> map) {
        return new RawInputMethodMap(map);
    }

    private RawInputMethodMap(@NonNull ArrayMap<String, InputMethodInfo> map) {
        mMap = map.isEmpty() ? EMPTY_MAP : new ArrayMap<>(map);
    }

    @AnyThread
    @NonNull
    List<InputMethodInfo> values() {
        return List.copyOf(mMap.values());
    }

    @NonNull
    InputMethodMap toInputMethodMap(@NonNull AdditionalSubtypeMap additionalSubtypeMap,
            @DirectBootAwareness int directBootAwareness, boolean userUnlocked) {
        final int size = mMap.size();
        final var newMap = new ArrayMap<String, InputMethodInfo>(size);

        final boolean requireDirectBootAwareFlag;
        switch (directBootAwareness) {
            case DirectBootAwareness.ANY -> requireDirectBootAwareFlag = false;
            case DirectBootAwareness.AUTO -> requireDirectBootAwareFlag = !userUnlocked;
            default -> {
                requireDirectBootAwareFlag = !userUnlocked;
                Slog.e(TAG, "Unknown directBootAwareness=" + directBootAwareness
                        + ". Falling back to DirectBootAwareness.AUTO");
            }
        }

        boolean updated = false;
        for (int i = 0; i < size; ++i) {
            final var imeId = mMap.keyAt(i);
            final var imi = mMap.valueAt(i);
            if (requireDirectBootAwareFlag && !imi.getServiceInfo().directBootAware) {
                updated = true;
                continue;
            }
            final var newAdditionalSubtypes = additionalSubtypeMap.get(imeId);
            if (newAdditionalSubtypes == null || newAdditionalSubtypes.isEmpty()) {
                newMap.put(imi.getId(), imi);
            } else {
                updated = true;
                newMap.put(imi.getId(), new InputMethodInfo(imi, newAdditionalSubtypes));
            }
        }
        // If newMap is semantically the same as mMap, we can reuse mMap and discard newMap.
        return InputMethodMap.of(updated ? newMap : mMap);
    }
}
+12 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import com.android.internal.inputmethod.IRemoteInputConnection;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;

/** Placeholder for all IMMS user specific fields */
final class UserData {
@@ -43,6 +44,17 @@ final class UserData {
    @NonNull
    final CountDownLatch mBackgroundLoadLatch = new CountDownLatch(1);

    /**
     * Contains non-null {@link RawInputMethodMap}, which represents the latest collections of
     * {@link android.view.inputmethod.InputMethodInfo} for both direct-boot aware and unaware IMEs
     * before taking {@link AdditionalSubtypeMap} into account.
     *
     * <p>See {@link RawInputMethodMap} for details on when to use this.</p>
     */
    @NonNull
    final AtomicReference<RawInputMethodMap> mRawInputMethodMap =
            new AtomicReference<>(RawInputMethodMap.emptyMap());

    @NonNull
    final InputMethodBindingController mBindingController;

+2 −2
Original line number Diff line number Diff line
@@ -122,8 +122,8 @@ public class InputMethodManagerServiceRestrictImeAmountTest extends

    private List<InputMethodInfo> filterInputMethodServices(List<ResolveInfo> resolveInfoList,
            List<String> enabledComponents) {
        final InputMethodMap methodMap = InputMethodManagerService.filterInputMethodServices(
                AdditionalSubtypeMap.EMPTY_MAP, enabledComponents, mContext, resolveInfoList);
        final var methodMap = InputMethodManagerService.filterInputMethodServices(
                enabledComponents, mContext, resolveInfoList);
        return methodMap.values();
    }

+9 −3
Original line number Diff line number Diff line
@@ -269,15 +269,21 @@ public class InputMethodManagerServiceTestBase {
        LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
        lifecycle.onStart();

        final var userData = mInputMethodManagerService.getUserData(mUserId);

        // Certain tests rely on TEST_IME_ID that is installed with AndroidTest.xml.
        // TODO(b/352615651): Consider just synthesizing test InputMethodInfo then injecting it.
        AdditionalSubtypeMapRepository.initializeIfNecessary(mUserId);
        final var settings = InputMethodManagerService.queryInputMethodServicesInternal(mContext,
                mUserId, AdditionalSubtypeMapRepository.get(mUserId), DirectBootAwareness.AUTO);
        final var rawMethodMap = InputMethodManagerService.queryRawInputMethodServiceMap(mContext,
                mUserId);
        userData.mRawInputMethodMap.set(rawMethodMap);
        final var settings = InputMethodSettings.create(rawMethodMap.toInputMethodMap(
                AdditionalSubtypeMap.EMPTY_MAP, DirectBootAwareness.AUTO, true /* userUnlocked */),
                mUserId);
        InputMethodSettingsRepository.put(mUserId, settings);

        // Emulate that the user initialization is done.
        mInputMethodManagerService.getUserData(mUserId).mBackgroundLoadLatch.countDown();
        userData.mBackgroundLoadLatch.countDown();

        // After this boot phase, services can broadcast Intents.
        lifecycle.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);