diff options
3 files changed, 121 insertions, 20 deletions
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index 879040622d2f..0cd2ba247a14 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -741,7 +741,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub */ int mImeWindowVis; - private LocaleList mLastSystemLocales; private final MyPackageMonitor mMyPackageMonitor = new MyPackageMonitor(); private final String mSlotIme; @@ -1199,9 +1198,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (Intent.ACTION_USER_ADDED.equals(action) || Intent.ACTION_USER_REMOVED.equals(action)) { updateCurrentProfileIds(); - return; - } else if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { - onActionLocaleChanged(); } else { Slog.w(TAG, "Unexpected intent " + intent); } @@ -1240,20 +1236,19 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub * * <p>Note: For historical reasons, {@link Intent#ACTION_LOCALE_CHANGED} has been sent to all * the users. We should ignore this event if this is about any background user's locale.</p> - * - * <p>Caution: This method must not be called when system is not ready.</p> */ - void onActionLocaleChanged() { + void onActionLocaleChanged(@NonNull LocaleList prevLocales, @NonNull LocaleList newLocales) { + if (DEBUG) { + Slog.d(TAG, "onActionLocaleChanged prev=" + prevLocales + " new=" + newLocales); + } synchronized (ImfLock.class) { - final LocaleList possibleNewLocale = mRes.getConfiguration().getLocales(); - if (possibleNewLocale != null && possibleNewLocale.equals(mLastSystemLocales)) { + if (!mSystemReady) { return; } buildInputMethodListLocked(true); // If the locale is changed, needs to reset the default ime resetDefaultImeLocked(mContext); updateFromSettingsLocked(true); - mLastSystemLocales = possibleNewLocale; } } @@ -1681,6 +1676,7 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub true /* allowIo */); thread.start(); mHandler = Handler.createAsync(thread.getLooper(), this); + SystemLocaleWrapper.onStart(context, this::onActionLocaleChanged, mHandler); mImeTrackerService = new ImeTrackerService(serviceThreadForTesting != null ? serviceThreadForTesting.getLooper() : Looper.getMainLooper()); // Note: SettingsObserver doesn't register observers in its constructor. @@ -1838,7 +1834,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub // Even in such cases, IMMS works fine because it will find the most applicable // IME for that user. final boolean initialUserSwitch = TextUtils.isEmpty(defaultImiId); - mLastSystemLocales = mRes.getConfiguration().getLocales(); // The mSystemReady flag is set during boot phase, // and user switch would not happen at that time. @@ -1890,7 +1885,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub } if (!mSystemReady) { mSystemReady = true; - mLastSystemLocales = mRes.getConfiguration().getLocales(); final int currentUserId = mSettings.getCurrentUserId(); mSettings.switchCurrentUser(currentUserId, !mUserManagerInternal.isUserUnlockingOrUnlocked(currentUserId)); @@ -1930,7 +1924,6 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub final IntentFilter broadcastFilterForSystemUser = new IntentFilter(); broadcastFilterForSystemUser.addAction(Intent.ACTION_USER_ADDED); broadcastFilterForSystemUser.addAction(Intent.ACTION_USER_REMOVED); - broadcastFilterForSystemUser.addAction(Intent.ACTION_LOCALE_CHANGED); mContext.registerReceiver(new ImmsBroadcastReceiverForSystemUser(), broadcastFilterForSystemUser); @@ -4070,7 +4063,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub && !TextUtils.isEmpty(mCurrentSubtype.getLocale())) { locale = mCurrentSubtype.getLocale(); } else { - locale = mRes.getConfiguration().locale.toString(); + locale = SystemLocaleWrapper.get(mSettings.getCurrentUserId()).get(0) + .toString(); } for (int i = 0; i < enabledCount; ++i) { final InputMethodInfo imi = enabled.get(i); @@ -5425,7 +5419,8 @@ public final class InputMethodManagerService extends IInputMethodManager.Stub if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { mCurrentSubtype = explicitlyOrImplicitlyEnabledSubtypes.get(0); } else if (explicitlyOrImplicitlyEnabledSubtypes.size() > 1) { - final String locale = mRes.getConfiguration().locale.toString(); + final String locale = SystemLocaleWrapper.get(mSettings.getCurrentUserId()) + .get(0).toString(); mCurrentSubtype = SubtypeUtils.findLastResortApplicableSubtypeLocked( explicitlyOrImplicitlyEnabledSubtypes, SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true); diff --git a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java index 831a2a2df2cd..c661c864b3ee 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodUtils.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodUtils.java @@ -219,7 +219,6 @@ final class InputMethodUtils { @NonNull private Context mUserAwareContext; - private Resources mRes; private ContentResolver mResolver; private final ArrayMap<String, InputMethodInfo> mMethodMap; @@ -282,7 +281,6 @@ final class InputMethodUtils { mUserAwareContext = context.getUserId() == userId ? context : context.createContextAsUser(UserHandle.of(userId), 0 /* flags */); - mRes = mUserAwareContext.getResources(); mResolver = mUserAwareContext.getContentResolver(); } @@ -399,7 +397,7 @@ final class InputMethodUtils { getEnabledInputMethodSubtypeListLocked(imi); if (allowsImplicitlyEnabledSubtypes && enabledSubtypes.isEmpty()) { enabledSubtypes = SubtypeUtils.getImplicitlyApplicableSubtypesLocked( - mRes.getConfiguration().getLocales(), imi); + SystemLocaleWrapper.get(mCurrentUserId), imi); } return InputMethodSubtype.sort(imi, enabledSubtypes); } @@ -648,7 +646,7 @@ final class InputMethodUtils { private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String, ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) { - final LocaleList localeList = mRes.getConfiguration().getLocales(); + final LocaleList localeList = SystemLocaleWrapper.get(mCurrentUserId); for (Pair<String, ArrayList<String>> enabledIme: enabledImes) { if (enabledIme.first.equals(imeId)) { final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second; @@ -851,7 +849,7 @@ final class InputMethodUtils { if (explicitlyOrImplicitlyEnabledSubtypes.size() == 1) { return explicitlyOrImplicitlyEnabledSubtypes.get(0); } - final String locale = mRes.getConfiguration().locale.toString(); + final String locale = SystemLocaleWrapper.get(mCurrentUserId).get(0).toString(); final InputMethodSubtype subtype = SubtypeUtils.findLastResortApplicableSubtypeLocked( explicitlyOrImplicitlyEnabledSubtypes, SubtypeUtils.SUBTYPE_MODE_KEYBOARD, locale, true); diff --git a/services/core/java/com/android/server/inputmethod/SystemLocaleWrapper.java b/services/core/java/com/android/server/inputmethod/SystemLocaleWrapper.java new file mode 100644 index 000000000000..0f1b7119f8ce --- /dev/null +++ b/services/core/java/com/android/server/inputmethod/SystemLocaleWrapper.java @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2023 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.annotation.UserIdInt; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.Handler; +import android.os.LocaleList; + +import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReference; + +/** + * A set of thread-safe utility methods for the system locals. + */ +final class SystemLocaleWrapper { + /** + * Not intended to be instantiated. + */ + private SystemLocaleWrapper() { + } + + private static final AtomicReference<LocaleList> sSystemLocale = + new AtomicReference<>(new LocaleList(Locale.getDefault())); + + /** + * Returns {@link LocaleList} for the specified user. + * + * <p>Note: If you call this method twice, it is possible that the second value is different + * from the first value. The caller is responsible for taking care of such cases.</p> + * + * @param userId the ID of the user to query about. + * @return {@link LocaleList} associated with the user. + */ + @AnyThread + @NonNull + static LocaleList get(@UserIdInt int userId) { + // Currently system locale is not per-user. + // TODO(b/30119489): Make this per-user. + return sSystemLocale.get(); + } + + /** + * Callback for the locale change event. When this gets filed, {@link #get(int)} is already + * updated to return the new value. + */ + interface Callback { + void onLocaleChanged(@NonNull LocaleList prevLocales, @NonNull LocaleList newLocales); + } + + /** + * Called when {@link InputMethodManagerService} is about to start. + * + * @param context {@link Context} to be used. + * @param callback {@link Callback} for the locale change events. + */ + @AnyThread + static void onStart(@NonNull Context context, @NonNull Callback callback, + @NonNull Handler handler) { + sSystemLocale.set(context.getResources().getConfiguration().getLocales()); + + context.registerReceiver(new LocaleChangeListener(context, callback), + new IntentFilter(Intent.ACTION_LOCALE_CHANGED), null, handler); + } + + private static final class LocaleChangeListener extends BroadcastReceiver { + @NonNull + private final Context mContext; + @NonNull + private final Callback mCallback; + LocaleChangeListener(@NonNull Context context, @NonNull Callback callback) { + mContext = context; + mCallback = callback; + } + + @Override + public void onReceive(Context context, Intent intent) { + if (!Intent.ACTION_LOCALE_CHANGED.equals(intent.getAction())) { + return; + } + final LocaleList newLocales = mContext.getResources().getConfiguration().getLocales(); + final LocaleList prevLocales = sSystemLocale.getAndSet(newLocales); + if (!Objects.equals(newLocales, prevLocales)) { + mCallback.onLocaleChanged(prevLocales, newLocales); + } + } + } +} |