1988323c57bd25a58f05dfa492d9b9c8ab62c5153satok/*
2988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Copyright (C) 2011 The Android Open Source Project
3988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
4988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Licensed under the Apache License, Version 2.0 (the "License");
5988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * you may not use this file except in compliance with the License.
6988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * You may obtain a copy of the License at
7988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
8988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *      http://www.apache.org/licenses/LICENSE-2.0
9988323c57bd25a58f05dfa492d9b9c8ab62c5153satok *
10988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * Unless required by applicable law or agreed to in writing, software
11988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * distributed under the License is distributed on an "AS IS" BASIS,
12988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * See the License for the specific language governing permissions and
14988323c57bd25a58f05dfa492d9b9c8ab62c5153satok * limitations under the License.
15988323c57bd25a58f05dfa492d9b9c8ab62c5153satok */
16988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
17988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpackage com.android.server;
18988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
193f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawaimport static android.view.textservice.TextServicesManager.DISABLE_PER_PROFILE_SPELL_CHECKER;
203f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa
213f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawaimport com.android.internal.annotations.GuardedBy;
22988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.content.PackageMonitor;
23174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawaimport com.android.internal.inputmethod.InputMethodUtils;
24988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerService;
257fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawaimport com.android.internal.textservice.ISpellCheckerServiceCallback;
26988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSession;
27988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSessionListener;
28988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ITextServicesManager;
29988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ITextServicesSessionListener;
303f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawaimport com.android.internal.textservice.LazyIntToIntMap;
31fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkeyimport com.android.internal.util.DumpUtils;
32988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
3303b2ea1102d9e3e9f189173878706ab04533eea3satokimport org.xmlpull.v1.XmlPullParserException;
3403b2ea1102d9e3e9f189173878706ab04533eea3satok
35f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawaimport android.annotation.NonNull;
3608ce18728cbdadbf376d2d1014daa06cf05c295aYohei Yukawaimport android.annotation.Nullable;
379faa2aeb8a0d40bd834b7a4bf6343fbae3a302e3Yohei Yukawaimport android.annotation.UserIdInt;
38988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.ComponentName;
3900d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataokaimport android.content.ContentResolver;
40988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.Context;
41988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.Intent;
42988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.ServiceConnection;
430f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncayimport android.content.pm.ApplicationInfo;
44988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.PackageManager;
45988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.ResolveInfo;
46988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.ServiceInfo;
4706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncayimport android.content.pm.UserInfo;
486be6d7548fb7c29a4d46dc985318ab2adf69f95fsatokimport android.os.Binder;
495357806980269d846a15c845a6fcc0384fb18860satokimport android.os.Bundle;
50988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.IBinder;
51f982e75a262db16e60fd3565638d399315dba761Guliz Tuncayimport android.os.RemoteCallbackList;
52988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.RemoteException;
5300d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataokaimport android.os.UserHandle;
54095fa371643b1d8e829067ea4ed93c357b39e773Yohei Yukawaimport android.os.UserManager;
55988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.provider.Settings;
56988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.service.textservice.SpellCheckerService;
575357806980269d846a15c845a6fcc0384fb18860satokimport android.text.TextUtils;
58988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.util.Slog;
5906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncayimport android.util.SparseArray;
6005f24700613fb4dce95fb6d5f8fe460d7a30c128satokimport android.view.inputmethod.InputMethodManager;
6105f24700613fb4dce95fb6d5f8fe460d7a30c128satokimport android.view.inputmethod.InputMethodSubtype;
62988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.SpellCheckerInfo;
63ada8c4e6a3da96a795f39a1028d448eb7aebfab3satokimport android.view.textservice.SpellCheckerSubtype;
64988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
6571e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackbornimport java.io.FileDescriptor;
6603b2ea1102d9e3e9f189173878706ab04533eea3satokimport java.io.IOException;
6771e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackbornimport java.io.PrintWriter;
68174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawaimport java.util.Arrays;
69988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.ArrayList;
70988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.HashMap;
71988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.List;
72174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawaimport java.util.Locale;
7371e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackbornimport java.util.Map;
7473ac947f6ea3cf12f88f48f181e7917d705997ebYohei Yukawaimport java.util.function.Predicate;
75988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
76988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpublic class TextServicesManagerService extends ITextServicesManager.Stub {
77988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static final String TAG = TextServicesManagerService.class.getSimpleName();
78988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private static final boolean DBG = false;
79988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
80988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final Context mContext;
81988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    private final TextServicesMonitor mMonitor;
8206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay    private final SparseArray<TextServicesData> mUserData = new SparseArray<>();
83f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa    @NonNull
84f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa    private final UserManager mUserManager;
8510ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay    private final Object mLock = new Object();
86988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
873f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa    @NonNull
883f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa    @GuardedBy("mLock")
893f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa    private final LazyIntToIntMap mSpellCheckerOwnerUserIdMap;
903f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa
9106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay    private static class TextServicesData {
9206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        @UserIdInt
9306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        private final int mUserId;
9406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap;
9506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        private final ArrayList<SpellCheckerInfo> mSpellCheckerList;
9606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups;
978e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private final Context mContext;
988e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private final ContentResolver mResolver;
998e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        public int mUpdateCount = 0;
10006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
1018e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        public TextServicesData(@UserIdInt int userId, @NonNull Context context) {
10206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mUserId = userId;
10306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mSpellCheckerMap = new HashMap<>();
10406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mSpellCheckerList = new ArrayList<>();
10506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mSpellCheckerBindGroups = new HashMap<>();
1068e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            mContext = context;
1078e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            mResolver = context.getContentResolver();
1088e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1098e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1108e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private void putString(final String key, final String str) {
1118e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            Settings.Secure.putStringForUser(mResolver, key, str, mUserId);
1128e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1138e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1148e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        @Nullable
1158e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private String getString(@NonNull final String key, @Nullable final String defaultValue) {
1168e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            final String result;
1178e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            result = Settings.Secure.getStringForUser(mResolver, key, mUserId);
1188e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return result != null ? result : defaultValue;
1198e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1208e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1218e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private void putInt(final String key, final int value) {
1228e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            Settings.Secure.putIntForUser(mResolver, key, value, mUserId);
1238e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1248e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1258e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private int getInt(final String key, final int defaultValue) {
1268e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return Settings.Secure.getIntForUser(mResolver, key, defaultValue, mUserId);
1278e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1288e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1298e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private boolean getBoolean(final String key, final boolean defaultValue) {
1308e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return getInt(key, defaultValue ? 1 : 0) == 1;
1318e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1328e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1338e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private void putSelectedSpellChecker(@Nullable String sciId) {
13483a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay            putString(Settings.Secure.SELECTED_SPELL_CHECKER, sciId);
1358e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1368e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1378e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private void putSelectedSpellCheckerSubtype(int hashCode) {
1388e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            putInt(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, hashCode);
1398e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1408e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1418e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        @NonNull
1428e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private String getSelectedSpellChecker() {
1438e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return getString(Settings.Secure.SELECTED_SPELL_CHECKER, "");
1448e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1458e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1468e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        public int getSelectedSpellCheckerSubtype(final int defaultValue) {
1478e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return getInt(Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, defaultValue);
1488e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1498e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1508e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        public boolean isSpellCheckerEnabled() {
1518e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return getBoolean(Settings.Secure.SPELL_CHECKER_ENABLED, true);
1528e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1538e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1548e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        @Nullable
1558e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        public SpellCheckerInfo getCurrentSpellChecker() {
1568e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            final String curSpellCheckerId = getSelectedSpellChecker();
1578e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            if (TextUtils.isEmpty(curSpellCheckerId)) {
1588e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                return null;
1598e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            }
1608e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return mSpellCheckerMap.get(curSpellCheckerId);
1618e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1628e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
16383a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay        public void setCurrentSpellChecker(@Nullable SpellCheckerInfo sci) {
16483a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay            if (sci != null) {
16583a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                putSelectedSpellChecker(sci.getId());
16683a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay            } else {
16783a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                putSelectedSpellChecker("");
16883a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay            }
1698e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            putSelectedSpellCheckerSubtype(SpellCheckerSubtype.SUBTYPE_ID_NONE);
1708e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
1718e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
1728e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private void initializeTextServicesData() {
1738e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            if (DBG) {
1748e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                Slog.d(TAG, "initializeTextServicesData for user: " + mUserId);
1758e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            }
1768e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            mSpellCheckerList.clear();
1778e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            mSpellCheckerMap.clear();
1788e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            mUpdateCount++;
1798e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            final PackageManager pm = mContext.getPackageManager();
1808e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            // Note: We do not specify PackageManager.MATCH_ENCRYPTION_* flags here because the
1818e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            // default behavior of PackageManager is exactly what we want.  It by default picks up
1828e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            // appropriate services depending on the unlock state for the specified user.
1838e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            final List<ResolveInfo> services = pm.queryIntentServicesAsUser(
1848e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA,
1858e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    mUserId);
1868e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            final int N = services.size();
1878e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            for (int i = 0; i < N; ++i) {
1888e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                final ResolveInfo ri = services.get(i);
1898e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                final ServiceInfo si = ri.serviceInfo;
1908e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                final ComponentName compName = new ComponentName(si.packageName, si.name);
1918e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                if (!android.Manifest.permission.BIND_TEXT_SERVICE.equals(si.permission)) {
1928e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    Slog.w(TAG, "Skipping text service " + compName
1938e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                            + ": it does not require the permission "
1948e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                            + android.Manifest.permission.BIND_TEXT_SERVICE);
1958e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    continue;
1968e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                }
1978e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                if (DBG) Slog.d(TAG, "Add: " + compName + " for user: " + mUserId);
1988e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                try {
1998e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    final SpellCheckerInfo sci = new SpellCheckerInfo(mContext, ri);
2008e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    if (sci.getSubtypeCount() <= 0) {
2018e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                        Slog.w(TAG, "Skipping text service " + compName
2028e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                                + ": it does not contain subtypes.");
2038e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                        continue;
2048e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    }
2058e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    mSpellCheckerList.add(sci);
2068e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    mSpellCheckerMap.put(sci.getId(), sci);
2078e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                } catch (XmlPullParserException e) {
2088e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    Slog.w(TAG, "Unable to load the spell checker " + compName, e);
2098e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                } catch (IOException e) {
2108e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    Slog.w(TAG, "Unable to load the spell checker " + compName, e);
2118e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                }
2128e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            }
2138e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            if (DBG) {
2148e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                Slog.d(TAG, "initializeSpellCheckerMap: " + mSpellCheckerList.size() + ","
2158e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                        + mSpellCheckerMap.size());
2168e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            }
2178e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        }
2188e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
2198e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        private void dump(PrintWriter pw) {
2208e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            int spellCheckerIndex = 0;
2218e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            pw.println("  User #" + mUserId);
2228e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            pw.println("  Spell Checkers:");
2238e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            pw.println("  Spell Checkers: " +  "mUpdateCount=" + mUpdateCount);
2248e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            for (final SpellCheckerInfo info : mSpellCheckerMap.values()) {
2258e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                pw.println("  Spell Checker #" + spellCheckerIndex);
2268e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                info.dump(pw, "    ");
2278e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                ++spellCheckerIndex;
2288e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            }
2298e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay
2308e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            pw.println("");
2318e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            pw.println("  Spell Checker Bind Groups:");
2328e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups = mSpellCheckerBindGroups;
2338e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            for (final Map.Entry<String, SpellCheckerBindGroup> ent
2348e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    : spellCheckerBindGroups.entrySet()) {
2358e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                final SpellCheckerBindGroup grp = ent.getValue();
2368e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                pw.println("    " + ent.getKey() + " " + grp + ":");
2378e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                pw.println("      " + "mInternalConnection=" + grp.mInternalConnection);
2388e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                pw.println("      " + "mSpellChecker=" + grp.mSpellChecker);
2398e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                pw.println("      " + "mUnbindCalled=" + grp.mUnbindCalled);
2408e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                pw.println("      " + "mConnected=" + grp.mConnected);
2418e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                final int numPendingSessionRequests = grp.mPendingSessionRequests.size();
2428e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                for (int j = 0; j < numPendingSessionRequests; j++) {
2438e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    final SessionRequest req = grp.mPendingSessionRequests.get(j);
2448e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("      " + "Pending Request #" + j + ":");
2458e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("        " + "mTsListener=" + req.mTsListener);
2468e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("        " + "mScListener=" + req.mScListener);
2478e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println(
248e46228420b9b5040779f9f828e44db12cd092317Yohei Yukawa                            "        " + "mScLocale=" + req.mLocale + " mUid=" + req.mUid);
2498e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                }
2508e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                final int numOnGoingSessionRequests = grp.mOnGoingSessionRequests.size();
2518e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                for (int j = 0; j < numOnGoingSessionRequests; j++) {
2528e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    final SessionRequest req = grp.mOnGoingSessionRequests.get(j);
2538e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("      " + "On going Request #" + j + ":");
2548e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    ++j;
2558e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("        " + "mTsListener=" + req.mTsListener);
2568e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("        " + "mScListener=" + req.mScListener);
2578e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println(
258e46228420b9b5040779f9f828e44db12cd092317Yohei Yukawa                            "        " + "mScLocale=" + req.mLocale + " mUid=" + req.mUid);
2598e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                }
2608e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                final int N = grp.mListeners.getRegisteredCallbackCount();
2618e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                for (int j = 0; j < N; j++) {
2628e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    final ISpellCheckerSessionListener mScListener =
2638e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                            grp.mListeners.getRegisteredCallbackItem(j);
2648e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("      " + "Listener #" + j + ":");
2658e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("        " + "mScListener=" + mScListener);
2668e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    pw.println("        " + "mGroup=" + grp);
2678e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                }
2688e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            }
26906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        }
27006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay    }
27106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
272a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa    public static final class Lifecycle extends SystemService {
273a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa        private TextServicesManagerService mService;
274a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa
275a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa        public Lifecycle(Context context) {
276a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa            super(context);
277a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa            mService = new TextServicesManagerService(context);
278a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa        }
279a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa
280a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa        @Override
281a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa        public void onStart() {
282a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa            publishBinderService(Context.TEXT_SERVICES_MANAGER_SERVICE, mService);
283a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa        }
284a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa
285a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa        @Override
28606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        public void onStopUser(@UserIdInt int userHandle) {
28706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (DBG) {
28806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                Slog.d(TAG, "onStopUser userId: " + userHandle);
28906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            }
29006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mService.onStopUser(userHandle);
2919faa2aeb8a0d40bd834b7a4bf6343fbae3a302e3Yohei Yukawa        }
2929faa2aeb8a0d40bd834b7a4bf6343fbae3a302e3Yohei Yukawa
2939faa2aeb8a0d40bd834b7a4bf6343fbae3a302e3Yohei Yukawa        @Override
294f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa        public void onUnlockUser(@UserIdInt int userHandle) {
29506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if(DBG) {
29606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                Slog.d(TAG, "onUnlockUser userId: " + userHandle);
29706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            }
298f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa            // Called on the system server's main looper thread.
299f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa            // TODO: Dispatch this to a worker thread as needed.
300f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa            mService.onUnlockUser(userHandle);
301f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa        }
302a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa    }
303a6a152e7de2b6db73474620c1eccda1ebe2eee9bYohei Yukawa
30406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay    void onStopUser(@UserIdInt int userId) {
30510ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay        synchronized (mLock) {
3063f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            // Clear user ID mapping table.
3073f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            mSpellCheckerOwnerUserIdMap.delete(userId);
3083f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa
30906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            // Clean per-user data
31006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            TextServicesData tsd = mUserData.get(userId);
31106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (tsd == null) return;
31206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
31306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            unbindServiceLocked(tsd);  // Remove bind groups first
31406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mUserData.remove(userId);  // This needs to be done after bind groups are all removed
315f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa        }
316f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa    }
317f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa
318f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa    void onUnlockUser(@UserIdInt int userId) {
31910ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay        synchronized (mLock) {
32006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            // Initialize internal state for the given user
32106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            initializeInternalStateLocked(userId);
322988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
323988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
324988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
325988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public TextServicesManagerService(Context context) {
326988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mContext = context;
327f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa        mUserManager = mContext.getSystemService(UserManager.class);
3283f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        mSpellCheckerOwnerUserIdMap = new LazyIntToIntMap(callingUserId -> {
3293f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            if (DISABLE_PER_PROFILE_SPELL_CHECKER) {
3303f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                final long token = Binder.clearCallingIdentity();
3313f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                try {
3323f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                    final UserInfo parent = mUserManager.getProfileParent(callingUserId);
3333f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                    return (parent != null) ? parent.id : callingUserId;
3343f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                } finally {
3353f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                    Binder.restoreCallingIdentity(token);
3363f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                }
3373f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            } else {
3383f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                return callingUserId;
3393f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            }
3403f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        });
341f0f168066335ac1ec103e575d693a957da714c4eYohei Yukawa
342988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        mMonitor = new TextServicesMonitor();
34306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        mMonitor.register(context, null, UserHandle.ALL, true);
34400d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka    }
34500d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka
3465e1d5d65f85bf6df783836ddc696c2df669809edGuliz Tuncay    private void initializeInternalStateLocked(@UserIdInt int userId) {
3473f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        // When DISABLE_PER_PROFILE_SPELL_CHECKER is true, we make sure here that work profile users
3483f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        // will never have non-null TextServicesData for their user ID.
3493f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        if (DISABLE_PER_PROFILE_SPELL_CHECKER
3503f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                && userId != mSpellCheckerOwnerUserIdMap.get(userId)) {
3513f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            return;
3523f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        }
3533f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa
35406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        TextServicesData tsd = mUserData.get(userId);
35506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        if (tsd == null) {
3568e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            tsd = new TextServicesData(userId, mContext);
35706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mUserData.put(userId, tsd);
35806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        }
35906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
3608e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        tsd.initializeTextServicesData();
3618e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay        SpellCheckerInfo sci = tsd.getCurrentSpellChecker();
362df5659d3d317b5cf351baffe3e0d4876e89678bfsatok        if (sci == null) {
3630f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay            sci = findAvailSystemSpellCheckerLocked(null, tsd);
36483a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay            // Set the current spell checker if there is one or more system spell checkers
36583a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay            // available. In this case, "sci" is the first one in the available spell
36683a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay            // checkers.
36783a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay            setCurrentSpellCheckerLocked(sci, tsd);
368df5659d3d317b5cf351baffe3e0d4876e89678bfsatok        }
369988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
370988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
37106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay    private final class TextServicesMonitor extends PackageMonitor {
372988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
373988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void onSomePackagesChanged() {
37406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            int userId = getChangingUserId();
37506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if(DBG) {
37606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                Slog.d(TAG, "onSomePackagesChanged: " + userId);
37700d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka            }
37806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
37910ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay            synchronized (mLock) {
38006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                TextServicesData tsd = mUserData.get(userId);
38106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                if (tsd == null) return;
38206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
383988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                // TODO: Update for each locale
3848e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                SpellCheckerInfo sci = tsd.getCurrentSpellChecker();
3858e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                tsd.initializeTextServicesData();
38683a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                // If spell checker is disabled, just return. The user should explicitly
38702260e2141fac0b6a712e4ca63702e657e8e7296Satoshi Kataoka                // enable the spell checker.
38883a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                if (!tsd.isSpellCheckerEnabled()) return;
38983a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay
39083a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                if (sci == null) {
39183a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                    sci = findAvailSystemSpellCheckerLocked(null, tsd);
39283a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                    // Set the current spell checker if there is one or more system spell checkers
39383a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                    // available. In this case, "sci" is the first one in the available spell
39483a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                    // checkers.
39583a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                    setCurrentSpellCheckerLocked(sci, tsd);
39683a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                } else {
39783a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                    final String packageName = sci.getPackageName();
39883a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                    final int change = isPackageDisappearing(packageName);
39983a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                    if (DBG) Slog.d(TAG, "Changing package name: " + packageName);
4004993f4ed1e5d7ce0658fd528de63a02375c61910Yohei Yukawa                    if (change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE) {
40183a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                        SpellCheckerInfo availSci =
40283a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                                findAvailSystemSpellCheckerLocked(packageName, tsd);
40383a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                        // Set the spell checker settings if different than before
40483a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                        if (availSci == null
40583a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                                || (availSci != null && !availSci.getId().equals(sci.getId()))) {
40683a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                            setCurrentSpellCheckerLocked(availSci, tsd);
40783a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay                        }
4085b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok                    }
409988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                }
410988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
411988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
412988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
413988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
41400d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka    private boolean bindCurrentSpellCheckerService(
41506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            Intent service, ServiceConnection conn, int flags, @UserIdInt int userId) {
41600d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka        if (service == null || conn == null) {
41706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn +
41806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    ", userId =" + userId);
41900d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka            return false;
42000d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka        }
42106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        return mContext.bindServiceAsUser(service, conn, flags, UserHandle.of(userId));
42200d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka    }
42300d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka
42406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay    private void unbindServiceLocked(TextServicesData tsd) {
42506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups = tsd.mSpellCheckerBindGroups;
42606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        for (SpellCheckerBindGroup scbg : spellCheckerBindGroups.values()) {
4271854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            scbg.removeAllLocked();
42800d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka        }
42906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        spellCheckerBindGroups.clear();
43000d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka    }
43100d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka
4320f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay    private SpellCheckerInfo findAvailSystemSpellCheckerLocked(String prefPackage,
43306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            TextServicesData tsd) {
4340f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay        // Filter the spell checker list to remove spell checker services that are not pre-installed
4350f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay        ArrayList<SpellCheckerInfo> spellCheckerList = new ArrayList<>();
4360f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay        for (SpellCheckerInfo sci : tsd.mSpellCheckerList) {
4370f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay            if ((sci.getServiceInfo().applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
4380f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay                spellCheckerList.add(sci);
4390f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay            }
4400f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay        }
4410f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay
44206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        final int spellCheckersCount = spellCheckerList.size();
443988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        if (spellCheckersCount == 0) {
444988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            Slog.w(TAG, "no available spell checker services found");
445988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            return null;
446988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
447988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        if (prefPackage != null) {
448988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            for (int i = 0; i < spellCheckersCount; ++i) {
44906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                final SpellCheckerInfo sci = spellCheckerList.get(i);
450988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                if (prefPackage.equals(sci.getPackageName())) {
451da317ef68603dc7649f98bda495267973825e7fasatok                    if (DBG) {
4520f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay                        Slog.d(TAG, "findAvailSystemSpellCheckerLocked: " + sci.getPackageName());
453da317ef68603dc7649f98bda495267973825e7fasatok                    }
454988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                    return sci;
455988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                }
456988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
457988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
458174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa
459174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        // Look up a spell checker based on the system locale.
460174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        // TODO: Still there is a room to improve in the following logic: e.g., check if the package
461174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        // is pre-installed or not.
462174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        final Locale systemLocal = mContext.getResources().getConfiguration().locale;
463174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        final ArrayList<Locale> suitableLocales =
464174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                InputMethodUtils.getSuitableLocalesForSpellChecker(systemLocal);
465174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        if (DBG) {
4660f0a37ba7def8db2c3e0ff1d7d8a30360e889f3fGuliz Tuncay            Slog.w(TAG, "findAvailSystemSpellCheckerLocked suitableLocales="
467174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                    + Arrays.toString(suitableLocales.toArray(new Locale[suitableLocales.size()])));
468174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        }
469174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        final int localeCount = suitableLocales.size();
470174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        for (int localeIndex = 0; localeIndex < localeCount; ++localeIndex) {
471174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa            final Locale locale = suitableLocales.get(localeIndex);
472174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa            for (int spellCheckersIndex = 0; spellCheckersIndex < spellCheckersCount;
473174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                    ++spellCheckersIndex) {
47406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                final SpellCheckerInfo info = spellCheckerList.get(spellCheckersIndex);
475174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                final int subtypeCount = info.getSubtypeCount();
476174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                for (int subtypeIndex = 0; subtypeIndex < subtypeCount; ++subtypeIndex) {
477174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                    final SpellCheckerSubtype subtype = info.getSubtypeAt(subtypeIndex);
478174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                    final Locale subtypeLocale = InputMethodUtils.constructLocaleFromString(
479174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                            subtype.getLocale());
480174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                    if (locale.equals(subtypeLocale)) {
481174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                        // TODO: We may have more spell checkers that fall into this category.
482174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                        // Ideally we should pick up the most suitable one instead of simply
483174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                        // returning the first found one.
484174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                        return info;
485174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                    }
486174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa                }
487174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa            }
488174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa        }
489174843afb629c57af19e14ee3ec4a91358061dd9Yohei Yukawa
490988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        if (spellCheckersCount > 1) {
491988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            Slog.w(TAG, "more than one spell checker service found, picking first");
492988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
49306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        return spellCheckerList.get(0);
494988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
495988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
496988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    // TODO: Save SpellCheckerService by supported languages. Currently only one spell
497988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    // checker is saved.
498988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    @Override
499988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public SpellCheckerInfo getCurrentSpellChecker(String locale) {
50006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        int userId = UserHandle.getCallingUserId();
50106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        synchronized (mLock) {
5023f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
50306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (tsd == null) return null;
50406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
5058e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return tsd.getCurrentSpellChecker();
506988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
507988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
508988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
5093cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok    // TODO: Respect allowImplicitlySelectedSubtype
51017150cf91be1478e367c2ef5e4f5baaa66b487d0Satoshi Kataoka    // TODO: Save SpellCheckerSubtype by supported languages by looking at "locale".
511ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok    @Override
5123cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok    public SpellCheckerSubtype getCurrentSpellCheckerSubtype(
5133cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok            String locale, boolean allowImplicitlySelectedSubtype) {
514e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        final int subtypeHashCode;
515e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        final SpellCheckerInfo sci;
516e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        final Locale systemLocale;
51706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        final int userId = UserHandle.getCallingUserId();
51806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
51910ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay        synchronized (mLock) {
5203f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
52106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (tsd == null) return null;
52206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
523e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            subtypeHashCode =
5248e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    tsd.getSelectedSpellCheckerSubtype(SpellCheckerSubtype.SUBTYPE_ID_NONE);
525ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok            if (DBG) {
526ad150ee271d9a0d27289407bd023924e23ec2b26Yohei Yukawa                Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCode);
527ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok            }
5288e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            sci = tsd.getCurrentSpellChecker();
529e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            systemLocale = mContext.getResources().getConfiguration().locale;
530e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        }
531e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        if (sci == null || sci.getSubtypeCount() == 0) {
532e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            if (DBG) {
533e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                Slog.w(TAG, "Subtype not found.");
534e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            }
535e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            return null;
536e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        }
537e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        if (subtypeHashCode == SpellCheckerSubtype.SUBTYPE_ID_NONE
538e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                && !allowImplicitlySelectedSubtype) {
539e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            return null;
540e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        }
541e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        String candidateLocale = null;
542e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        if (subtypeHashCode == 0) {
543e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            // Spell checker language settings == "auto"
544e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            final InputMethodManager imm = mContext.getSystemService(InputMethodManager.class);
545e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            if (imm != null) {
546e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                final InputMethodSubtype currentInputMethodSubtype =
547e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                        imm.getCurrentInputMethodSubtype();
548e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                if (currentInputMethodSubtype != null) {
549e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                    final String localeString = currentInputMethodSubtype.getLocale();
550e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                    if (!TextUtils.isEmpty(localeString)) {
551e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                        // 1. Use keyboard locale if available in the spell checker
552e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                        candidateLocale = localeString;
553e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                    }
554a33c4fc5bed76727f1e06e522e0136101a2304cdsatok                }
555ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok            }
556e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            if (candidateLocale == null) {
557e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                // 2. Use System locale if available in the spell checker
558e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                candidateLocale = systemLocale.toString();
559fbedf1a3978d5dfc4a886e4c7107d4bc1923f740satok            }
560e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        }
561e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        SpellCheckerSubtype candidate = null;
562e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        for (int i = 0; i < sci.getSubtypeCount(); ++i) {
563e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            final SpellCheckerSubtype scs = sci.getSubtypeAt(i);
564ad150ee271d9a0d27289407bd023924e23ec2b26Yohei Yukawa            if (subtypeHashCode == 0) {
565e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                final String scsLocale = scs.getLocale();
566e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                if (candidateLocale.equals(scsLocale)) {
567e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                    return scs;
568e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                } else if (candidate == null) {
569e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                    if (candidateLocale.length() >= 2 && scsLocale.length() >= 2
570e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                            && candidateLocale.startsWith(scsLocale)) {
571e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                        // Fall back to the applicable language
572e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                        candidate = scs;
57305f24700613fb4dce95fb6d5f8fe460d7a30c128satok                    }
57405f24700613fb4dce95fb6d5f8fe460d7a30c128satok                }
575e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa            } else if (scs.hashCode() == subtypeHashCode) {
576e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                if (DBG) {
577e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                    Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale
578e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                            + ", " + scs.getLocale());
579ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok                }
580e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                // 3. Use the user specified spell check language
581e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa                return scs;
582ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok            }
583ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok        }
584e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        // 4. Fall back to the applicable language and return it if not null
585e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        // 5. Simply just return it even if it's null which means we could find no suitable
586e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        // spell check languages
587e3e31a880169a5d685ea4bf7efae81430a62b4feYohei Yukawa        return candidate;
588ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok    }
589ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok
590988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    @Override
5915b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok    public void getSpellCheckerService(String sciId, String locale,
5925357806980269d846a15c845a6fcc0384fb18860satok            ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
5935357806980269d846a15c845a6fcc0384fb18860satok            Bundle bundle) {
5945b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok        if (TextUtils.isEmpty(sciId) || tsListener == null || scListener == null) {
595988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            Slog.e(TAG, "getSpellCheckerService: Invalid input.");
596988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            return;
597988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
59806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        int callingUserId = UserHandle.getCallingUserId();
59906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
60010ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay        synchronized (mLock) {
6013f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId);
60206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (tsd == null) return;
60306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
60406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            HashMap<String, SpellCheckerInfo> spellCheckerMap = tsd.mSpellCheckerMap;
60506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (!spellCheckerMap.containsKey(sciId)) {
606988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                return;
607988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
60806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            final SpellCheckerInfo sci = spellCheckerMap.get(sciId);
60906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups =
61006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    tsd.mSpellCheckerBindGroups;
61106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            SpellCheckerBindGroup bindGroup = spellCheckerBindGroups.get(sciId);
612df5659d3d317b5cf351baffe3e0d4876e89678bfsatok            final int uid = Binder.getCallingUid();
6137fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            if (bindGroup == null) {
6147fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                final long ident = Binder.clearCallingIdentity();
6157fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                try {
61606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    bindGroup = startSpellCheckerServiceInnerLocked(sci, tsd);
6177fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                } finally {
6187fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                    Binder.restoreCallingIdentity(ident);
6197fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                }
6207fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                if (bindGroup == null) {
6217fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                    // startSpellCheckerServiceInnerLocked failed.
6227fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                    return;
6236be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok                }
624988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
6257fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
6267fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            // Start getISpellCheckerSession async IPC, or just queue the request until the spell
6277fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            // checker service is bound.
6287fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            bindGroup.getISpellCheckerSessionOrQueueLocked(
6295e1d5d65f85bf6df783836ddc696c2df669809edGuliz Tuncay                    new SessionRequest(uid, locale, tsListener, scListener, bundle));
630988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
631988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
632988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
633a33c4fc5bed76727f1e06e522e0136101a2304cdsatok    @Override
634a33c4fc5bed76727f1e06e522e0136101a2304cdsatok    public boolean isSpellCheckerEnabled() {
63506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        int userId = UserHandle.getCallingUserId();
63606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
63710ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay        synchronized (mLock) {
6383f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
63906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (tsd == null) return false;
64006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
6418e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            return tsd.isSpellCheckerEnabled();
642a33c4fc5bed76727f1e06e522e0136101a2304cdsatok        }
643a33c4fc5bed76727f1e06e522e0136101a2304cdsatok    }
644a33c4fc5bed76727f1e06e522e0136101a2304cdsatok
6457fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa    @Nullable
64606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay    private SpellCheckerBindGroup startSpellCheckerServiceInnerLocked(SpellCheckerInfo info,
64706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            TextServicesData tsd) {
648df5659d3d317b5cf351baffe3e0d4876e89678bfsatok        if (DBG) {
649df5659d3d317b5cf351baffe3e0d4876e89678bfsatok            Slog.w(TAG, "Start spell checker session inner locked.");
650df5659d3d317b5cf351baffe3e0d4876e89678bfsatok        }
6516be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        final String sciId = info.getId();
65206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        final InternalServiceConnection connection = new InternalServiceConnection(sciId,
65306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                tsd.mSpellCheckerBindGroups);
6546be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE);
6556be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        serviceIntent.setComponent(info.getComponent());
6566be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        if (DBG) {
6576be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            Slog.w(TAG, "bind service: " + info.getId());
6586be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        }
659d69e4c1460017062e7c36be55801cb434ad19d97Dianne Hackborn        if (!bindCurrentSpellCheckerService(serviceIntent, connection,
66006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                Context.BIND_AUTO_CREATE | Context.BIND_IMPORTANT_BACKGROUND, tsd.mUserId)) {
6616be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            Slog.e(TAG, "Failed to get a spell checker service.");
6627fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            return null;
6636be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        }
6647fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        final SpellCheckerBindGroup group = new SpellCheckerBindGroup(connection);
66506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
66606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        tsd.mSpellCheckerBindGroups.put(sciId, group);
6677fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        return group;
6686be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok    }
6696be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok
670988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    @Override
671562ab585f9e413d9696ee250e5ec02f95889a157satok    public SpellCheckerInfo[] getEnabledSpellCheckers() {
67206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        int callingUserId = UserHandle.getCallingUserId();
67306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
67406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        synchronized (mLock) {
6753f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            final TextServicesData tsd = getDataFromCallingUserIdLocked(callingUserId);
67606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (tsd == null) return null;
67706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
67806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            ArrayList<SpellCheckerInfo> spellCheckerList = tsd.mSpellCheckerList;
67906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (DBG) {
68006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                Slog.d(TAG, "getEnabledSpellCheckers: " + spellCheckerList.size());
68106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                for (int i = 0; i < spellCheckerList.size(); ++i) {
68206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    Slog.d(TAG,
68306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                            "EnabledSpellCheckers: " + spellCheckerList.get(i).getPackageName());
68406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                }
685da317ef68603dc7649f98bda495267973825e7fasatok            }
68606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            return spellCheckerList.toArray(new SpellCheckerInfo[spellCheckerList.size()]);
687da317ef68603dc7649f98bda495267973825e7fasatok        }
688562ab585f9e413d9696ee250e5ec02f95889a157satok    }
689562ab585f9e413d9696ee250e5ec02f95889a157satok
690562ab585f9e413d9696ee250e5ec02f95889a157satok    @Override
691988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    public void finishSpellCheckerService(ISpellCheckerSessionListener listener) {
692da317ef68603dc7649f98bda495267973825e7fasatok        if (DBG) {
693da317ef68603dc7649f98bda495267973825e7fasatok            Slog.d(TAG, "FinishSpellCheckerService");
694da317ef68603dc7649f98bda495267973825e7fasatok        }
69506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        int userId = UserHandle.getCallingUserId();
69606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
69710ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay        synchronized (mLock) {
6983f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            final TextServicesData tsd = getDataFromCallingUserIdLocked(userId);
69906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (tsd == null) return;
70006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
701074637f1626a56031bcb98b8a88345c8f57cbb77Yohei Yukawa            final ArrayList<SpellCheckerBindGroup> removeList = new ArrayList<>();
70206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups =
70306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    tsd.mSpellCheckerBindGroups;
70406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            for (SpellCheckerBindGroup group : spellCheckerBindGroups.values()) {
705988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                if (group == null) continue;
7064c3fa642ef1c0e1bb3303d4b25d8af4e6a7d08a2satok                // Use removeList to avoid modifying mSpellCheckerBindGroups in this loop.
7074c3fa642ef1c0e1bb3303d4b25d8af4e6a7d08a2satok                removeList.add(group);
7084c3fa642ef1c0e1bb3303d4b25d8af4e6a7d08a2satok            }
7094c3fa642ef1c0e1bb3303d4b25d8af4e6a7d08a2satok            final int removeSize = removeList.size();
7104c3fa642ef1c0e1bb3303d4b25d8af4e6a7d08a2satok            for (int i = 0; i < removeSize; ++i) {
7114c3fa642ef1c0e1bb3303d4b25d8af4e6a7d08a2satok                removeList.get(i).removeListener(listener);
712988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
713988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
714988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
715988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
71683a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay    private void setCurrentSpellCheckerLocked(@Nullable SpellCheckerInfo sci, TextServicesData tsd) {
71783a7330bbe8ee1e8fcaa88ba2fb5e4cb3154c790Guliz Tuncay        final String sciId = (sci != null) ? sci.getId() : "";
718562ab585f9e413d9696ee250e5ec02f95889a157satok        if (DBG) {
7195b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok            Slog.w(TAG, "setCurrentSpellChecker: " + sciId);
720562ab585f9e413d9696ee250e5ec02f95889a157satok        }
721df5659d3d317b5cf351baffe3e0d4876e89678bfsatok        final long ident = Binder.clearCallingIdentity();
722df5659d3d317b5cf351baffe3e0d4876e89678bfsatok        try {
7238e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay            tsd.setCurrentSpellChecker(sci);
724a33c4fc5bed76727f1e06e522e0136101a2304cdsatok        } finally {
725a33c4fc5bed76727f1e06e522e0136101a2304cdsatok            Binder.restoreCallingIdentity(ident);
726a33c4fc5bed76727f1e06e522e0136101a2304cdsatok        }
727a33c4fc5bed76727f1e06e522e0136101a2304cdsatok    }
728a33c4fc5bed76727f1e06e522e0136101a2304cdsatok
72971e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackborn    @Override
73071e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackborn    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
731fe9a53bc45fd0124a876dc0a49680aaf86641d3eJeff Sharkey        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
73271e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackborn
73302f646e6a0b7353e86fd44b22cb933c9ee2f89c4Yohei Yukawa        if (args.length == 0 || (args.length == 1 && args[0].equals("-a"))) {
73402f646e6a0b7353e86fd44b22cb933c9ee2f89c4Yohei Yukawa            // Dump all users' data
73506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            synchronized (mLock) {
73606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                pw.println("Current Text Services Manager state:");
73706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                pw.println("  Users:");
73806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                final int numOfUsers = mUserData.size();
73906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                for (int i = 0; i < numOfUsers; i++) {
7408e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    TextServicesData tsd = mUserData.valueAt(i);
7418e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    tsd.dump(pw);
74206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                }
74371e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackborn            }
74406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        } else {  // Dump a given user's data
74506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            if (args.length != 2 || !args[0].equals("--user")) {
74606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                pw.println("Invalid arguments to text services." );
74706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                return;
74806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            } else {
74906a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                int userId = Integer.parseInt(args[1]);
75006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                UserInfo userInfo = mUserManager.getUserInfo(userId);
75106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                if (userInfo == null) {
75206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    pw.println("Non-existent user.");
75306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    return;
7547fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                }
75506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                TextServicesData tsd = mUserData.get(userId);
75606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                if (tsd == null) {
75706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    pw.println("User needs to unlock first." );
75806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    return;
7597fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                }
76006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                synchronized (mLock) {
76106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    pw.println("Current Text Services Manager state:");
76206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                    pw.println("  User " + userId + ":");
7638e6fa02f46f1df00d3d4bfa4f7fe3d8a98da7ce5Guliz Tuncay                    tsd.dump(pw);
76471e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackborn                }
76571e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackborn            }
76606a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        }
76706a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay    }
76806a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
7693f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa    /**
7703f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa     * @param callingUserId user ID of the calling process
7713f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa     * @return {@link TextServicesData} for the given user.  {@code null} if spell checker is not
7723f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa     *         temporarily / permanently available for the specified user
7733f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa     */
7743f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa    @Nullable
7753f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa    private TextServicesData getDataFromCallingUserIdLocked(@UserIdInt int callingUserId) {
7763f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        final int spellCheckerOwnerUserId = mSpellCheckerOwnerUserIdMap.get(callingUserId);
7773f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        final TextServicesData data = mUserData.get(spellCheckerOwnerUserId);
7783f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        if (DISABLE_PER_PROFILE_SPELL_CHECKER) {
7793f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            if (spellCheckerOwnerUserId != callingUserId) {
7803f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                // Calling process is running under child profile.
7813f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                if (data == null) {
7823f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                    return null;
7833f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                }
7843f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                final SpellCheckerInfo info = data.getCurrentSpellChecker();
7853f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                if (info == null) {
7863f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                    return null;
7873f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                }
7883f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                final ServiceInfo serviceInfo = info.getServiceInfo();
7893f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                if ((serviceInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
7903f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                    // To be conservative, non pre-installed spell checker services are not allowed
7913f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                    // to be used for child profiles.
7923f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                    return null;
7933f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa                }
7943f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa            }
7953f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        }
7963f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa        return data;
7973f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa    }
7983f8c568883e5df9dd0c44d19c5c4d23edd3756c3Yohei Yukawa
7997fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa    private static final class SessionRequest {
800e46228420b9b5040779f9f828e44db12cd092317Yohei Yukawa        public final int mUid;
8017fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        @Nullable
8027fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        public final String mLocale;
8037fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        @NonNull
8047fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        public final ITextServicesSessionListener mTsListener;
8057fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        @NonNull
8067fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        public final ISpellCheckerSessionListener mScListener;
8077fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        @Nullable
8087fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        public final Bundle mBundle;
8097fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
810e46228420b9b5040779f9f828e44db12cd092317Yohei Yukawa        SessionRequest(int uid, @Nullable String locale,
8117fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                @NonNull ITextServicesSessionListener tsListener,
8127fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                @NonNull ISpellCheckerSessionListener scListener, @Nullable Bundle bundle) {
813e46228420b9b5040779f9f828e44db12cd092317Yohei Yukawa            mUid = uid;
8147fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mLocale = locale;
8157fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mTsListener = tsListener;
8167fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mScListener = scListener;
8177fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mBundle = bundle;
8187fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        }
8197fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa    }
8207fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
821988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    // SpellCheckerBindGroup contains active text service session listeners.
822988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from
823988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    // mSpellCheckerBindGroups
82406b4be771cc59bcc64e86d5763eee41431a03523Yohei Yukawa    private final class SpellCheckerBindGroup {
825df5659d3d317b5cf351baffe3e0d4876e89678bfsatok        private final String TAG = SpellCheckerBindGroup.class.getSimpleName();
8266be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        private final InternalServiceConnection mInternalConnection;
827f982e75a262db16e60fd3565638d399315dba761Guliz Tuncay        private final InternalDeathRecipients mListeners;
8287fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        private boolean mUnbindCalled;
8297fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        private ISpellCheckerService mSpellChecker;
8307fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        private boolean mConnected;
8317fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        private final ArrayList<SessionRequest> mPendingSessionRequests = new ArrayList<>();
8327fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        private final ArrayList<SessionRequest> mOnGoingSessionRequests = new ArrayList<>();
83306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        @NonNull
83406a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups;
83506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
8367fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
8377fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        public SpellCheckerBindGroup(InternalServiceConnection connection) {
838988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mInternalConnection = connection;
839f982e75a262db16e60fd3565638d399315dba761Guliz Tuncay            mListeners = new InternalDeathRecipients(this);
84006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mSpellCheckerBindGroups = connection.mSpellCheckerBindGroups;
841988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
842988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
8431854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa        public void onServiceConnectedLocked(ISpellCheckerService spellChecker) {
844da317ef68603dc7649f98bda495267973825e7fasatok            if (DBG) {
8451854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa                Slog.d(TAG, "onServiceConnectedLocked");
846da317ef68603dc7649f98bda495267973825e7fasatok            }
8474e713f14419a37f385cf1509b011982bdcf67edcsatok
8484163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa            if (mUnbindCalled) {
8494163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                return;
8504163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa            }
8511854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            mSpellChecker = spellChecker;
8521854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            mConnected = true;
8531854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            // Dispatch pending getISpellCheckerSession requests.
8544163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa            try {
8554163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                final int size = mPendingSessionRequests.size();
8564163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                for (int i = 0; i < size; ++i) {
8574163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                    final SessionRequest request = mPendingSessionRequests.get(i);
8584163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                    mSpellChecker.getISpellCheckerSession(
8594163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                            request.mLocale, request.mScListener, request.mBundle,
8604163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                            new ISpellCheckerServiceCallbackBinder(this, request));
8614163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                    mOnGoingSessionRequests.add(request);
8624163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                }
8637fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                mPendingSessionRequests.clear();
8644163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa            } catch(RemoteException e) {
8654163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                // The target spell checker service is not available.  Better to reset the state.
8664163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa                removeAllLocked();
867988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
8684163a96fa21d83d236e27d4a7150aa229a6dfeffYohei Yukawa            cleanLocked();
869988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
870988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
8711854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa        public void onServiceDisconnectedLocked() {
872787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay            if (DBG) {
8731854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa                Slog.d(TAG, "onServiceDisconnectedLocked");
874787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay            }
875787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay
8761854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            mSpellChecker = null;
8771854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            mConnected = false;
878787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay        }
879787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay
880988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void removeListener(ISpellCheckerSessionListener listener) {
881da317ef68603dc7649f98bda495267973825e7fasatok            if (DBG) {
882df5659d3d317b5cf351baffe3e0d4876e89678bfsatok                Slog.w(TAG, "remove listener: " + listener.hashCode());
883da317ef68603dc7649f98bda495267973825e7fasatok            }
88410ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay            synchronized (mLock) {
885f982e75a262db16e60fd3565638d399315dba761Guliz Tuncay                mListeners.unregister(listener);
88673ac947f6ea3cf12f88f48f181e7917d705997ebYohei Yukawa                final IBinder scListenerBinder = listener.asBinder();
88773ac947f6ea3cf12f88f48f181e7917d705997ebYohei Yukawa                final Predicate<SessionRequest> removeCondition =
88873ac947f6ea3cf12f88f48f181e7917d705997ebYohei Yukawa                        request -> request.mScListener.asBinder() == scListenerBinder;
88973ac947f6ea3cf12f88f48f181e7917d705997ebYohei Yukawa                mPendingSessionRequests.removeIf(removeCondition);
89073ac947f6ea3cf12f88f48f181e7917d705997ebYohei Yukawa                mOnGoingSessionRequests.removeIf(removeCondition);
891988323c57bd25a58f05dfa492d9b9c8ab62c5153satok                cleanLocked();
892988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
893988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
894988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
8954c3fa642ef1c0e1bb3303d4b25d8af4e6a7d08a2satok        // cleanLocked may remove elements from mSpellCheckerBindGroups
896988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private void cleanLocked() {
897da317ef68603dc7649f98bda495267973825e7fasatok            if (DBG) {
898da317ef68603dc7649f98bda495267973825e7fasatok                Slog.d(TAG, "cleanLocked");
899da317ef68603dc7649f98bda495267973825e7fasatok            }
9007fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            if (mUnbindCalled) {
9017fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                return;
9027fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            }
9037fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            // If there are no more active listeners, clean up.  Only do this once.
904f982e75a262db16e60fd3565638d399315dba761Guliz Tuncay            if (mListeners.getRegisteredCallbackCount() > 0) {
9057fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                return;
9067fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            }
9077fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            if (!mPendingSessionRequests.isEmpty()) {
9087fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                return;
9097fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            }
9107fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            if (!mOnGoingSessionRequests.isEmpty()) {
9117fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                return;
9127fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            }
9137fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            final String sciId = mInternalConnection.mSciId;
9147fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            final SpellCheckerBindGroup cur = mSpellCheckerBindGroups.get(sciId);
9157fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            if (cur == this) {
9167fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                if (DBG) {
9177fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                    Slog.d(TAG, "Remove bind group.");
9186be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok                }
9197fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                mSpellCheckerBindGroups.remove(sciId);
920988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
9217fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mContext.unbindService(mInternalConnection);
9227fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mUnbindCalled = true;
923988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
9246be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok
9251854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa        public void removeAllLocked() {
9266be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok            Slog.e(TAG, "Remove the spell checker bind unexpectedly.");
9271854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            final int size = mListeners.getRegisteredCallbackCount();
9281854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            for (int i = size - 1; i >= 0; --i) {
9291854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa                mListeners.unregister(mListeners.getRegisteredCallbackItem(i));
9307fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            }
9311854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            mPendingSessionRequests.clear();
9321854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            mOnGoingSessionRequests.clear();
9331854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa            cleanLocked();
9347fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        }
9357fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
9367fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        public void getISpellCheckerSessionOrQueueLocked(@NonNull SessionRequest request) {
9377fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            if (mUnbindCalled) {
9387fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                return;
9397fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            }
94073ac947f6ea3cf12f88f48f181e7917d705997ebYohei Yukawa            mListeners.register(request.mScListener);
9417fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            if (!mConnected) {
9427fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                mPendingSessionRequests.add(request);
9437fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                return;
9447fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            }
9457fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            try {
9467fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                mSpellChecker.getISpellCheckerSession(
9477fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                        request.mLocale, request.mScListener, request.mBundle,
9487fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                        new ISpellCheckerServiceCallbackBinder(this, request));
9497fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                mOnGoingSessionRequests.add(request);
9507fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            } catch(RemoteException e) {
9517fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                // The target spell checker service is not available.  Better to reset the state.
9521854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa                removeAllLocked();
9537fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            }
9547fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            cleanLocked();
9557fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        }
9567fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
9577fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        void onSessionCreated(@Nullable final ISpellCheckerSession newSession,
9587fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                @NonNull final SessionRequest request) {
95910ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay            synchronized (mLock) {
9607fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                if (mUnbindCalled) {
9617fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                    return;
9627fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                }
9637fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                if (mOnGoingSessionRequests.remove(request)) {
9647fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                    try {
9657fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                        request.mTsListener.onServiceConnected(newSession);
9667fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                    } catch (RemoteException e) {
9677fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                        // Technically this can happen if the spell checker client app is already
9687fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                        // dead.  We can just forget about this request; the request is already
9697fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                        // removed from mOnGoingSessionRequests and the death recipient listener is
9707fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                        // not yet added to mListeners. There is nothing to release further.
9717fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                    }
9727fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                }
973df5659d3d317b5cf351baffe3e0d4876e89678bfsatok                cleanLocked();
974df5659d3d317b5cf351baffe3e0d4876e89678bfsatok            }
9756be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok        }
976988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
977988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
97806b4be771cc59bcc64e86d5763eee41431a03523Yohei Yukawa    private final class InternalServiceConnection implements ServiceConnection {
979988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private final String mSciId;
98006a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        @NonNull
98106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups;
98206a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay        public InternalServiceConnection(String id,
98306a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay                @NonNull HashMap<String, SpellCheckerBindGroup> spellCheckerBindGroups) {
984988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mSciId = id;
98506a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay            mSpellCheckerBindGroups = spellCheckerBindGroups;
986988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
987988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
988988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
989988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void onServiceConnected(ComponentName name, IBinder service) {
99010ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay            synchronized (mLock) {
99100d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka                onServiceConnectedInnerLocked(name, service);
99200d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka            }
99300d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka        }
99400d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka
99500d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka        private void onServiceConnectedInnerLocked(ComponentName name, IBinder service) {
99600d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka            if (DBG) {
9971854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa                Slog.w(TAG, "onServiceConnectedInnerLocked: " + name);
99800d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka            }
99900d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka            final ISpellCheckerService spellChecker =
100000d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka                    ISpellCheckerService.Stub.asInterface(service);
100106a2624049f09e1ad68f8ab74fdb525aacd5c1e5Guliz Tuncay
100200d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka            final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
100300d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka            if (group != null && this == group.mInternalConnection) {
10041854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa                group.onServiceConnectedLocked(spellChecker);
1005988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            }
1006988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
1007988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
1008988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
1009988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        public void onServiceDisconnected(ComponentName name) {
101010ae385e29a0cc49e59b6ce53f803c938c1ec277Guliz Tuncay            synchronized (mLock) {
1011787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay                onServiceDisconnectedInnerLocked(name);
1012787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay            }
1013787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay        }
1014787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay
1015787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay        private void onServiceDisconnectedInnerLocked(ComponentName name) {
1016787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay            if (DBG) {
10171854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa                Slog.w(TAG, "onServiceDisconnectedInnerLocked: " + name);
1018787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay            }
1019787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay            final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId);
1020787aa8c0db993e8f624769382c7395dd2f369ac4Guliz Tuncay            if (group != null && this == group.mInternalConnection) {
10211854cb5aedaef2b3e6bbec0c69d148b148b3ecaeYohei Yukawa                group.onServiceDisconnectedLocked();
102271e14da93c8fea512fedf1b5226d28cd2c330238Dianne Hackborn            }
1023988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
1024988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
1025988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
1026f14fe14a3bd09ac60d70e33248eae88c4da08a66Yohei Yukawa    private static final class InternalDeathRecipients extends
1027f982e75a262db16e60fd3565638d399315dba761Guliz Tuncay            RemoteCallbackList<ISpellCheckerSessionListener> {
1028988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        private final SpellCheckerBindGroup mGroup;
10297fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
1030f982e75a262db16e60fd3565638d399315dba761Guliz Tuncay        public InternalDeathRecipients(SpellCheckerBindGroup group) {
1031988323c57bd25a58f05dfa492d9b9c8ab62c5153satok            mGroup = group;
1032988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
1033988323c57bd25a58f05dfa492d9b9c8ab62c5153satok
1034988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        @Override
1035f982e75a262db16e60fd3565638d399315dba761Guliz Tuncay        public void onCallbackDied(ISpellCheckerSessionListener listener) {
1036f14fe14a3bd09ac60d70e33248eae88c4da08a66Yohei Yukawa            mGroup.removeListener(listener);
1037988323c57bd25a58f05dfa492d9b9c8ab62c5153satok        }
1038988323c57bd25a58f05dfa492d9b9c8ab62c5153satok    }
103900d2d4125e4944ebcabdecd423573ee00f716293Satoshi Kataoka
10407fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa    private static final class ISpellCheckerServiceCallbackBinder
10417fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            extends ISpellCheckerServiceCallback.Stub {
10427fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        @NonNull
10437fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        private final SpellCheckerBindGroup mBindGroup;
10447fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        @NonNull
10457fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        private final SessionRequest mRequest;
10467fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
10477fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        ISpellCheckerServiceCallbackBinder(@NonNull final SpellCheckerBindGroup bindGroup,
10487fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa                @NonNull final SessionRequest request) {
10497fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mBindGroup = bindGroup;
10507fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mRequest = request;
10517fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        }
10527fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa
10537fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        @Override
10547fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        public void onSessionCreated(@Nullable ISpellCheckerSession newSession) {
10557fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa            mBindGroup.onSessionCreated(newSession, mRequest);
10567fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa        }
10577fa65eef203c4ed3ce00ddef96ccf311d3bfb58cYohei Yukawa    }
1058988323c57bd25a58f05dfa492d9b9c8ab62c5153satok}
1059