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