TextServicesManagerService.java revision fbedf1a3978d5dfc4a886e4c7107d4bc1923f740
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 19988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.content.PackageMonitor; 20988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerService; 21988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSession; 22988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ISpellCheckerSessionListener; 23988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ITextServicesManager; 24988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport com.android.internal.textservice.ITextServicesSessionListener; 25988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 2603b2ea1102d9e3e9f189173878706ab04533eea3satokimport org.xmlpull.v1.XmlPullParserException; 2703b2ea1102d9e3e9f189173878706ab04533eea3satok 28988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.ComponentName; 29988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.Context; 30988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.Intent; 31988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.ServiceConnection; 32988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.PackageManager; 33988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.ResolveInfo; 34988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.content.pm.ServiceInfo; 356be6d7548fb7c29a4d46dc985318ab2adf69f95fsatokimport android.os.Binder; 365357806980269d846a15c845a6fcc0384fb18860satokimport android.os.Bundle; 37988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.IBinder; 38988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.os.RemoteException; 39988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.provider.Settings; 40988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.service.textservice.SpellCheckerService; 415357806980269d846a15c845a6fcc0384fb18860satokimport android.text.TextUtils; 42988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.util.Slog; 43988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport android.view.textservice.SpellCheckerInfo; 44ada8c4e6a3da96a795f39a1028d448eb7aebfab3satokimport android.view.textservice.SpellCheckerSubtype; 45988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 4603b2ea1102d9e3e9f189173878706ab04533eea3satokimport java.io.IOException; 47988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.ArrayList; 48988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.HashMap; 49988323c57bd25a58f05dfa492d9b9c8ab62c5153satokimport java.util.List; 50988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 51988323c57bd25a58f05dfa492d9b9c8ab62c5153satokpublic class TextServicesManagerService extends ITextServicesManager.Stub { 52988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final String TAG = TextServicesManagerService.class.getSimpleName(); 53988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static final boolean DBG = false; 54988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 55988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final Context mContext; 56988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private boolean mSystemReady; 57988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final TextServicesMonitor mMonitor; 58988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap = 59988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new HashMap<String, SpellCheckerInfo>(); 60988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<SpellCheckerInfo>(); 61988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = 62988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new HashMap<String, SpellCheckerBindGroup>(); 63988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 64988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void systemReady() { 65988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!mSystemReady) { 66988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSystemReady = true; 67988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 68988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 69988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 70988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public TextServicesManagerService(Context context) { 71988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSystemReady = false; 72988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mContext = context; 73988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mMonitor = new TextServicesMonitor(); 74988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mMonitor.register(context, true); 75988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized (mSpellCheckerMap) { 76988323c57bd25a58f05dfa492d9b9c8ab62c5153satok buildSpellCheckerMapLocked(context, mSpellCheckerList, mSpellCheckerMap); 77988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 78df5659d3d317b5cf351baffe3e0d4876e89678bfsatok SpellCheckerInfo sci = getCurrentSpellChecker(null); 79df5659d3d317b5cf351baffe3e0d4876e89678bfsatok if (sci == null) { 80df5659d3d317b5cf351baffe3e0d4876e89678bfsatok sci = findAvailSpellCheckerLocked(null, null); 81df5659d3d317b5cf351baffe3e0d4876e89678bfsatok if (sci != null) { 82df5659d3d317b5cf351baffe3e0d4876e89678bfsatok // Set the current spell checker if there is one or more spell checkers 83df5659d3d317b5cf351baffe3e0d4876e89678bfsatok // available. In this case, "sci" is the first one in the available spell 84df5659d3d317b5cf351baffe3e0d4876e89678bfsatok // checkers. 855b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok setCurrentSpellCheckerLocked(sci.getId()); 86df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 87df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 88988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 89988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 90988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private class TextServicesMonitor extends PackageMonitor { 91988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 92988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onSomePackagesChanged() { 93988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized (mSpellCheckerMap) { 94988323c57bd25a58f05dfa492d9b9c8ab62c5153satok buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap); 95988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // TODO: Update for each locale 96988323c57bd25a58f05dfa492d9b9c8ab62c5153satok SpellCheckerInfo sci = getCurrentSpellChecker(null); 97da317ef68603dc7649f98bda495267973825e7fasatok if (sci == null) return; 98988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final String packageName = sci.getPackageName(); 99988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int change = isPackageDisappearing(packageName); 1005b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok if (// Package disappearing 1015b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE 1025b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok // Package modified 1035b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok || isPackageModified(packageName)) { 1045b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok sci = findAvailSpellCheckerLocked(null, packageName); 1055b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok if (sci != null) { 1065b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok setCurrentSpellCheckerLocked(sci.getId()); 1075b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok } 108988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 109988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 110988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 111988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 112988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 113988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private static void buildSpellCheckerMapLocked(Context context, 114988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map) { 115988323c57bd25a58f05dfa492d9b9c8ab62c5153satok list.clear(); 116988323c57bd25a58f05dfa492d9b9c8ab62c5153satok map.clear(); 117988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final PackageManager pm = context.getPackageManager(); 118988323c57bd25a58f05dfa492d9b9c8ab62c5153satok List<ResolveInfo> services = pm.queryIntentServices( 119988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA); 120988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int N = services.size(); 121988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < N; ++i) { 122988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ResolveInfo ri = services.get(i); 123988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ServiceInfo si = ri.serviceInfo; 124988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ComponentName compName = new ComponentName(si.packageName, si.name); 125988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!android.Manifest.permission.BIND_TEXT_SERVICE.equals(si.permission)) { 126988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.w(TAG, "Skipping text service " + compName 127988323c57bd25a58f05dfa492d9b9c8ab62c5153satok + ": it does not require the permission " 128988323c57bd25a58f05dfa492d9b9c8ab62c5153satok + android.Manifest.permission.BIND_TEXT_SERVICE); 129988323c57bd25a58f05dfa492d9b9c8ab62c5153satok continue; 130988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 131988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (DBG) Slog.d(TAG, "Add: " + compName); 13203b2ea1102d9e3e9f189173878706ab04533eea3satok try { 13303b2ea1102d9e3e9f189173878706ab04533eea3satok final SpellCheckerInfo sci = new SpellCheckerInfo(context, ri); 1343cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok if (sci.getSubtypeCount() <= 0) { 1353cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok Slog.w(TAG, "Skipping text service " + compName 1363cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok + ": it does not contain subtypes."); 1373cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok continue; 1383cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok } 13903b2ea1102d9e3e9f189173878706ab04533eea3satok list.add(sci); 14003b2ea1102d9e3e9f189173878706ab04533eea3satok map.put(sci.getId(), sci); 14103b2ea1102d9e3e9f189173878706ab04533eea3satok } catch (XmlPullParserException e) { 14203b2ea1102d9e3e9f189173878706ab04533eea3satok Slog.w(TAG, "Unable to load the spell checker " + compName, e); 14303b2ea1102d9e3e9f189173878706ab04533eea3satok } catch (IOException e) { 14403b2ea1102d9e3e9f189173878706ab04533eea3satok Slog.w(TAG, "Unable to load the spell checker " + compName, e); 14503b2ea1102d9e3e9f189173878706ab04533eea3satok } 146988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 147da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 148da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "buildSpellCheckerMapLocked: " + list.size() + "," + map.size()); 149da317ef68603dc7649f98bda495267973825e7fasatok } 150988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 151988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 152988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // TODO: find an appropriate spell checker for specified locale 153988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private SpellCheckerInfo findAvailSpellCheckerLocked(String locale, String prefPackage) { 154988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int spellCheckersCount = mSpellCheckerList.size(); 155988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (spellCheckersCount == 0) { 156988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.w(TAG, "no available spell checker services found"); 157988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return null; 158988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 159988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (prefPackage != null) { 160988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < spellCheckersCount; ++i) { 161988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final SpellCheckerInfo sci = mSpellCheckerList.get(i); 162988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (prefPackage.equals(sci.getPackageName())) { 163da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 164da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "findAvailSpellCheckerLocked: " + sci.getPackageName()); 165da317ef68603dc7649f98bda495267973825e7fasatok } 166988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return sci; 167988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 168988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 169988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 170988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (spellCheckersCount > 1) { 171988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.w(TAG, "more than one spell checker service found, picking first"); 172988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 173988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mSpellCheckerList.get(0); 174988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 175988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 176988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // TODO: Save SpellCheckerService by supported languages. Currently only one spell 177988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // checker is saved. 178988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 179988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerInfo getCurrentSpellChecker(String locale) { 180988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized (mSpellCheckerMap) { 181a33c4fc5bed76727f1e06e522e0136101a2304cdsatok final String curSpellCheckerId = 182988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Settings.Secure.getString(mContext.getContentResolver(), 183ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok Settings.Secure.SELECTED_SPELL_CHECKER); 184562ab585f9e413d9696ee250e5ec02f95889a157satok if (DBG) { 185562ab585f9e413d9696ee250e5ec02f95889a157satok Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId); 186562ab585f9e413d9696ee250e5ec02f95889a157satok } 187988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (TextUtils.isEmpty(curSpellCheckerId)) { 188df5659d3d317b5cf351baffe3e0d4876e89678bfsatok return null; 189988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 190988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return mSpellCheckerMap.get(curSpellCheckerId); 191988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 192988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 193988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 1943cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok // TODO: Respect allowImplicitlySelectedSubtype 195ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok // TODO: Save SpellCheckerSubtype by supported languages. 196ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok @Override 1973cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok public SpellCheckerSubtype getCurrentSpellCheckerSubtype( 1983cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok String locale, boolean allowImplicitlySelectedSubtype) { 199ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok synchronized (mSpellCheckerMap) { 200ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok final String subtypeHashCodeStr = 201ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok Settings.Secure.getString(mContext.getContentResolver(), 202ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE); 203ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok if (DBG) { 204ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok Slog.w(TAG, "getCurrentSpellChecker: " + subtypeHashCodeStr); 205ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 206ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok final SpellCheckerInfo sci = getCurrentSpellChecker(null); 207a33c4fc5bed76727f1e06e522e0136101a2304cdsatok if (sci == null || sci.getSubtypeCount() == 0) { 208a33c4fc5bed76727f1e06e522e0136101a2304cdsatok if (DBG) { 209a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Slog.w(TAG, "Subtype not found."); 210a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 211ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok return null; 212ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 213ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok if (TextUtils.isEmpty(subtypeHashCodeStr)) { 214a33c4fc5bed76727f1e06e522e0136101a2304cdsatok if (DBG) { 215a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Slog.w(TAG, "Return first subtype in " + sci.getId()); 216a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 2173cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok return null; 218ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 219ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok final int hashCode = Integer.valueOf(subtypeHashCodeStr); 220fbedf1a3978d5dfc4a886e4c7107d4bc1923f740satok if (hashCode == 0) { 221fbedf1a3978d5dfc4a886e4c7107d4bc1923f740satok return null; 222fbedf1a3978d5dfc4a886e4c7107d4bc1923f740satok } 223ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok for (int i = 0; i < sci.getSubtypeCount(); ++i) { 224ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok final SpellCheckerSubtype scs = sci.getSubtypeAt(i); 225ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok if (scs.hashCode() == hashCode) { 226a33c4fc5bed76727f1e06e522e0136101a2304cdsatok if (DBG) { 227a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Slog.w(TAG, "Return subtype " + scs.hashCode()); 228a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 229ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok return scs; 230ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 231ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 232a33c4fc5bed76727f1e06e522e0136101a2304cdsatok if (DBG) { 233a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Slog.w(TAG, "Return first subtype in " + sci.getId()); 234a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 2353cb5b39a0e63d98c4e7b47e9a5b5758e9d4024bdsatok return null; 236ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 237ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 238ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok 239988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 2405b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok public void getSpellCheckerService(String sciId, String locale, 2415357806980269d846a15c845a6fcc0384fb18860satok ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener, 2425357806980269d846a15c845a6fcc0384fb18860satok Bundle bundle) { 243988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!mSystemReady) { 244988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 245988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 2465b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok if (TextUtils.isEmpty(sciId) || tsListener == null || scListener == null) { 247988323c57bd25a58f05dfa492d9b9c8ab62c5153satok Slog.e(TAG, "getSpellCheckerService: Invalid input."); 248988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 249988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 250988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 251988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (!mSpellCheckerMap.containsKey(sciId)) { 252988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 253988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 2545b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok final SpellCheckerInfo sci = mSpellCheckerMap.get(sciId); 255df5659d3d317b5cf351baffe3e0d4876e89678bfsatok final int uid = Binder.getCallingUid(); 256988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mSpellCheckerBindGroups.containsKey(sciId)) { 2576be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok final SpellCheckerBindGroup bindGroup = mSpellCheckerBindGroups.get(sciId); 2586be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (bindGroup != null) { 2596be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok final InternalDeathRecipient recipient = 2606be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok mSpellCheckerBindGroups.get(sciId).addListener( 2615357806980269d846a15c845a6fcc0384fb18860satok tsListener, locale, scListener, uid, bundle); 2626be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (recipient == null) { 2636be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 2646be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Slog.w(TAG, "Didn't create a death recipient."); 2656be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 2666be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok return; 2676be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 2686be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (bindGroup.mSpellChecker == null & bindGroup.mConnected) { 2696be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Slog.e(TAG, "The state of the spell checker bind group is illegal."); 2706be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok bindGroup.removeAll(); 2716be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } else if (bindGroup.mSpellChecker != null) { 2726be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 273df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Slog.w(TAG, "Existing bind found. Return a spell checker session now. " 274df5659d3d317b5cf351baffe3e0d4876e89678bfsatok + "Listeners count = " + bindGroup.mListeners.size()); 2756be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 2766be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok try { 2776be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok final ISpellCheckerSession session = 2786be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok bindGroup.mSpellChecker.getISpellCheckerSession( 2795357806980269d846a15c845a6fcc0384fb18860satok recipient.mScLocale, recipient.mScListener, bundle); 280df5659d3d317b5cf351baffe3e0d4876e89678bfsatok if (session != null) { 281df5659d3d317b5cf351baffe3e0d4876e89678bfsatok tsListener.onServiceConnected(session); 282df5659d3d317b5cf351baffe3e0d4876e89678bfsatok return; 283df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } else { 284df5659d3d317b5cf351baffe3e0d4876e89678bfsatok if (DBG) { 285df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Slog.w(TAG, "Existing bind already expired. "); 286df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 287df5659d3d317b5cf351baffe3e0d4876e89678bfsatok bindGroup.removeAll(); 288df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 2896be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } catch (RemoteException e) { 2906be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Slog.e(TAG, "Exception in getting spell checker session: " + e); 2916be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok bindGroup.removeAll(); 2926be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 2936be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 2946be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 295988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 2966be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok final long ident = Binder.clearCallingIdentity(); 2976be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok try { 2985357806980269d846a15c845a6fcc0384fb18860satok startSpellCheckerServiceInnerLocked( 2995357806980269d846a15c845a6fcc0384fb18860satok sci, locale, tsListener, scListener, uid, bundle); 3006be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } finally { 3016be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Binder.restoreCallingIdentity(ident); 302988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 303988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 304988323c57bd25a58f05dfa492d9b9c8ab62c5153satok return; 305988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 306988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 307a33c4fc5bed76727f1e06e522e0136101a2304cdsatok @Override 308a33c4fc5bed76727f1e06e522e0136101a2304cdsatok public boolean isSpellCheckerEnabled() { 309a33c4fc5bed76727f1e06e522e0136101a2304cdsatok synchronized(mSpellCheckerMap) { 310a33c4fc5bed76727f1e06e522e0136101a2304cdsatok return isSpellCheckerEnabledLocked(); 311a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 312a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 313a33c4fc5bed76727f1e06e522e0136101a2304cdsatok 3146be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok private void startSpellCheckerServiceInnerLocked(SpellCheckerInfo info, String locale, 315df5659d3d317b5cf351baffe3e0d4876e89678bfsatok ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener, 3165357806980269d846a15c845a6fcc0384fb18860satok int uid, Bundle bundle) { 317df5659d3d317b5cf351baffe3e0d4876e89678bfsatok if (DBG) { 318df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Slog.w(TAG, "Start spell checker session inner locked."); 319df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 3206be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok final String sciId = info.getId(); 3216be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok final InternalServiceConnection connection = new InternalServiceConnection( 3225357806980269d846a15c845a6fcc0384fb18860satok sciId, locale, scListener, bundle); 3236be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE); 3246be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok serviceIntent.setComponent(info.getComponent()); 3256be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 3266be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Slog.w(TAG, "bind service: " + info.getId()); 3276be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 3286be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (!mContext.bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) { 3296be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Slog.e(TAG, "Failed to get a spell checker service."); 3306be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok return; 3316be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 3326be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok final SpellCheckerBindGroup group = new SpellCheckerBindGroup( 3335357806980269d846a15c845a6fcc0384fb18860satok connection, tsListener, locale, scListener, uid, bundle); 3346be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok mSpellCheckerBindGroups.put(sciId, group); 3356be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 3366be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok 337988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 338562ab585f9e413d9696ee250e5ec02f95889a157satok public SpellCheckerInfo[] getEnabledSpellCheckers() { 339da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 340da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "getEnabledSpellCheckers: " + mSpellCheckerList.size()); 341da317ef68603dc7649f98bda495267973825e7fasatok for (int i = 0; i < mSpellCheckerList.size(); ++i) { 342da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "EnabledSpellCheckers: " + mSpellCheckerList.get(i).getPackageName()); 343da317ef68603dc7649f98bda495267973825e7fasatok } 344da317ef68603dc7649f98bda495267973825e7fasatok } 345562ab585f9e413d9696ee250e5ec02f95889a157satok return mSpellCheckerList.toArray(new SpellCheckerInfo[mSpellCheckerList.size()]); 346562ab585f9e413d9696ee250e5ec02f95889a157satok } 347562ab585f9e413d9696ee250e5ec02f95889a157satok 348562ab585f9e413d9696ee250e5ec02f95889a157satok @Override 349988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void finishSpellCheckerService(ISpellCheckerSessionListener listener) { 350da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 351da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "FinishSpellCheckerService"); 352da317ef68603dc7649f98bda495267973825e7fasatok } 353988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 354988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (SpellCheckerBindGroup group : mSpellCheckerBindGroups.values()) { 355988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (group == null) continue; 356988323c57bd25a58f05dfa492d9b9c8ab62c5153satok group.removeListener(listener); 357988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 358988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 359988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 360988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 361df5659d3d317b5cf351baffe3e0d4876e89678bfsatok @Override 362ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok public void setCurrentSpellChecker(String locale, String sciId) { 363df5659d3d317b5cf351baffe3e0d4876e89678bfsatok synchronized(mSpellCheckerMap) { 364df5659d3d317b5cf351baffe3e0d4876e89678bfsatok if (mContext.checkCallingOrSelfPermission( 365df5659d3d317b5cf351baffe3e0d4876e89678bfsatok android.Manifest.permission.WRITE_SECURE_SETTINGS) 366df5659d3d317b5cf351baffe3e0d4876e89678bfsatok != PackageManager.PERMISSION_GRANTED) { 367df5659d3d317b5cf351baffe3e0d4876e89678bfsatok throw new SecurityException( 368df5659d3d317b5cf351baffe3e0d4876e89678bfsatok "Requires permission " 369df5659d3d317b5cf351baffe3e0d4876e89678bfsatok + android.Manifest.permission.WRITE_SECURE_SETTINGS); 370df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 3715b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok setCurrentSpellCheckerLocked(sciId); 372df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 373df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 374df5659d3d317b5cf351baffe3e0d4876e89678bfsatok 375ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok @Override 376ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok public void setCurrentSpellCheckerSubtype(String locale, int hashCode) { 377ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok synchronized(mSpellCheckerMap) { 378ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok if (mContext.checkCallingOrSelfPermission( 379ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok android.Manifest.permission.WRITE_SECURE_SETTINGS) 380ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok != PackageManager.PERMISSION_GRANTED) { 381ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok throw new SecurityException( 382ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok "Requires permission " 383ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok + android.Manifest.permission.WRITE_SECURE_SETTINGS); 384ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 385a33c4fc5bed76727f1e06e522e0136101a2304cdsatok setCurrentSpellCheckerSubtypeLocked(hashCode); 386a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 387a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 388a33c4fc5bed76727f1e06e522e0136101a2304cdsatok 389a33c4fc5bed76727f1e06e522e0136101a2304cdsatok @Override 390a33c4fc5bed76727f1e06e522e0136101a2304cdsatok public void setSpellCheckerEnabled(boolean enabled) { 391a33c4fc5bed76727f1e06e522e0136101a2304cdsatok synchronized(mSpellCheckerMap) { 392a33c4fc5bed76727f1e06e522e0136101a2304cdsatok if (mContext.checkCallingOrSelfPermission( 393a33c4fc5bed76727f1e06e522e0136101a2304cdsatok android.Manifest.permission.WRITE_SECURE_SETTINGS) 394a33c4fc5bed76727f1e06e522e0136101a2304cdsatok != PackageManager.PERMISSION_GRANTED) { 395a33c4fc5bed76727f1e06e522e0136101a2304cdsatok throw new SecurityException( 396a33c4fc5bed76727f1e06e522e0136101a2304cdsatok "Requires permission " 397a33c4fc5bed76727f1e06e522e0136101a2304cdsatok + android.Manifest.permission.WRITE_SECURE_SETTINGS); 398a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 399a33c4fc5bed76727f1e06e522e0136101a2304cdsatok setSpellCheckerEnabledLocked(enabled); 400ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 401ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 402ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok 4035b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok private void setCurrentSpellCheckerLocked(String sciId) { 404562ab585f9e413d9696ee250e5ec02f95889a157satok if (DBG) { 4055b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok Slog.w(TAG, "setCurrentSpellChecker: " + sciId); 406562ab585f9e413d9696ee250e5ec02f95889a157satok } 4075b9b5a9553a0276dc6b7f1f458c8d4ed03227988satok if (TextUtils.isEmpty(sciId) || !mSpellCheckerMap.containsKey(sciId)) return; 408df5659d3d317b5cf351baffe3e0d4876e89678bfsatok final long ident = Binder.clearCallingIdentity(); 409df5659d3d317b5cf351baffe3e0d4876e89678bfsatok try { 410df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Settings.Secure.putString(mContext.getContentResolver(), 411ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok Settings.Secure.SELECTED_SPELL_CHECKER, sciId); 412ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } finally { 413ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok Binder.restoreCallingIdentity(ident); 414ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 415ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 416ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok 417a33c4fc5bed76727f1e06e522e0136101a2304cdsatok private void setCurrentSpellCheckerSubtypeLocked(int hashCode) { 418ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok if (DBG) { 419ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok Slog.w(TAG, "setCurrentSpellCheckerSubtype: " + hashCode); 420ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 421ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok final SpellCheckerInfo sci = getCurrentSpellChecker(null); 422fbedf1a3978d5dfc4a886e4c7107d4bc1923f740satok int tempHashCode = 0; 423fbedf1a3978d5dfc4a886e4c7107d4bc1923f740satok for (int i = 0; sci != null && i < sci.getSubtypeCount(); ++i) { 424ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok if(sci.getSubtypeAt(i).hashCode() == hashCode) { 425fbedf1a3978d5dfc4a886e4c7107d4bc1923f740satok tempHashCode = hashCode; 426ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok break; 427ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 428ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok } 429ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok final long ident = Binder.clearCallingIdentity(); 430ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok try { 431ada8c4e6a3da96a795f39a1028d448eb7aebfab3satok Settings.Secure.putString(mContext.getContentResolver(), 432fbedf1a3978d5dfc4a886e4c7107d4bc1923f740satok Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(tempHashCode)); 433df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } finally { 434df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Binder.restoreCallingIdentity(ident); 435df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 436988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 437988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 438a33c4fc5bed76727f1e06e522e0136101a2304cdsatok private void setSpellCheckerEnabledLocked(boolean enabled) { 439a33c4fc5bed76727f1e06e522e0136101a2304cdsatok if (DBG) { 440a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Slog.w(TAG, "setSpellCheckerEnabled: " + enabled); 441a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 442a33c4fc5bed76727f1e06e522e0136101a2304cdsatok final long ident = Binder.clearCallingIdentity(); 443a33c4fc5bed76727f1e06e522e0136101a2304cdsatok try { 444a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Settings.Secure.putInt(mContext.getContentResolver(), 445a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Settings.Secure.SPELL_CHECKER_ENABLED, enabled ? 1 : 0); 446a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } finally { 447a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Binder.restoreCallingIdentity(ident); 448a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 449a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 450a33c4fc5bed76727f1e06e522e0136101a2304cdsatok 451a33c4fc5bed76727f1e06e522e0136101a2304cdsatok private boolean isSpellCheckerEnabledLocked() { 452a33c4fc5bed76727f1e06e522e0136101a2304cdsatok final long ident = Binder.clearCallingIdentity(); 453a33c4fc5bed76727f1e06e522e0136101a2304cdsatok try { 454a33c4fc5bed76727f1e06e522e0136101a2304cdsatok final boolean retval = Settings.Secure.getInt(mContext.getContentResolver(), 455a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Settings.Secure.SPELL_CHECKER_ENABLED, 1) == 1; 456a33c4fc5bed76727f1e06e522e0136101a2304cdsatok if (DBG) { 457a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Slog.w(TAG, "getSpellCheckerEnabled: " + retval); 458a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 459a33c4fc5bed76727f1e06e522e0136101a2304cdsatok return retval; 460a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } finally { 461a33c4fc5bed76727f1e06e522e0136101a2304cdsatok Binder.restoreCallingIdentity(ident); 462a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 463a33c4fc5bed76727f1e06e522e0136101a2304cdsatok } 464a33c4fc5bed76727f1e06e522e0136101a2304cdsatok 465988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // SpellCheckerBindGroup contains active text service session listeners. 466988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from 467988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // mSpellCheckerBindGroups 468988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private class SpellCheckerBindGroup { 469df5659d3d317b5cf351baffe3e0d4876e89678bfsatok private final String TAG = SpellCheckerBindGroup.class.getSimpleName(); 4706be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok private final InternalServiceConnection mInternalConnection; 4716be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok private final ArrayList<InternalDeathRecipient> mListeners = 472988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new ArrayList<InternalDeathRecipient>(); 4736be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok public ISpellCheckerService mSpellChecker; 4746be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok public boolean mConnected; 475988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 476988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public SpellCheckerBindGroup(InternalServiceConnection connection, 477988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ITextServicesSessionListener listener, String locale, 4785357806980269d846a15c845a6fcc0384fb18860satok ISpellCheckerSessionListener scListener, int uid, Bundle bundle) { 479988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mInternalConnection = connection; 4806be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok mConnected = false; 4815357806980269d846a15c845a6fcc0384fb18860satok addListener(listener, locale, scListener, uid, bundle); 482988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 483988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 484988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onServiceConnected(ISpellCheckerService spellChecker) { 485da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 486da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "onServiceConnected"); 487da317ef68603dc7649f98bda495267973825e7fasatok } 488988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 489988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (InternalDeathRecipient listener : mListeners) { 490988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 491988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ISpellCheckerSession session = spellChecker.getISpellCheckerSession( 4925357806980269d846a15c845a6fcc0384fb18860satok listener.mScLocale, listener.mScListener, listener.mBundle); 493988323c57bd25a58f05dfa492d9b9c8ab62c5153satok listener.mTsListener.onServiceConnected(session); 494988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch (RemoteException e) { 495df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Slog.e(TAG, "Exception in getting the spell checker session: " + e); 4966be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok removeAll(); 4976be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok return; 498988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 499988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 5006be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok mSpellChecker = spellChecker; 5016be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok mConnected = true; 502988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 503988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 504988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 5056be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok public InternalDeathRecipient addListener(ITextServicesSessionListener tsListener, 5065357806980269d846a15c845a6fcc0384fb18860satok String locale, ISpellCheckerSessionListener scListener, int uid, Bundle bundle) { 507da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 508da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "addListener: " + locale); 509da317ef68603dc7649f98bda495267973825e7fasatok } 5106be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok InternalDeathRecipient recipient = null; 511988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 512988323c57bd25a58f05dfa492d9b9c8ab62c5153satok try { 513988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int size = mListeners.size(); 514988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < size; ++i) { 515988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mListeners.get(i).hasSpellCheckerListener(scListener)) { 516988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // do not add the lister if the group already contains this. 5176be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok return null; 518988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 519988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 5206be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok recipient = new InternalDeathRecipient( 5215357806980269d846a15c845a6fcc0384fb18860satok this, tsListener, locale, scListener, uid, bundle); 522988323c57bd25a58f05dfa492d9b9c8ab62c5153satok scListener.asBinder().linkToDeath(recipient, 0); 523df5659d3d317b5cf351baffe3e0d4876e89678bfsatok mListeners.add(recipient); 524988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } catch(RemoteException e) { 525988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // do nothing 526988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 527988323c57bd25a58f05dfa492d9b9c8ab62c5153satok cleanLocked(); 528988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 5296be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok return recipient; 530988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 531988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 532988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void removeListener(ISpellCheckerSessionListener listener) { 533da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 534df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Slog.w(TAG, "remove listener: " + listener.hashCode()); 535da317ef68603dc7649f98bda495267973825e7fasatok } 536988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 537988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int size = mListeners.size(); 538988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final ArrayList<InternalDeathRecipient> removeList = 539988323c57bd25a58f05dfa492d9b9c8ab62c5153satok new ArrayList<InternalDeathRecipient>(); 540988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < size; ++i) { 541988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final InternalDeathRecipient tempRecipient = mListeners.get(i); 542988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if(tempRecipient.hasSpellCheckerListener(listener)) { 543df5659d3d317b5cf351baffe3e0d4876e89678bfsatok if (DBG) { 544df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Slog.w(TAG, "found existing listener."); 545df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 546988323c57bd25a58f05dfa492d9b9c8ab62c5153satok removeList.add(tempRecipient); 547988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 548988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 549988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final int removeSize = removeList.size(); 550988323c57bd25a58f05dfa492d9b9c8ab62c5153satok for (int i = 0; i < removeSize; ++i) { 551df5659d3d317b5cf351baffe3e0d4876e89678bfsatok if (DBG) { 552df5659d3d317b5cf351baffe3e0d4876e89678bfsatok Slog.w(TAG, "Remove " + removeList.get(i)); 553df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 554988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mListeners.remove(removeList.get(i)); 555988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 556988323c57bd25a58f05dfa492d9b9c8ab62c5153satok cleanLocked(); 557988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 558988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 559988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 560988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private void cleanLocked() { 561da317ef68603dc7649f98bda495267973825e7fasatok if (DBG) { 562da317ef68603dc7649f98bda495267973825e7fasatok Slog.d(TAG, "cleanLocked"); 563da317ef68603dc7649f98bda495267973825e7fasatok } 564988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (mListeners.isEmpty()) { 5656be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (mSpellCheckerBindGroups.containsKey(this)) { 5666be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok mSpellCheckerBindGroups.remove(this); 5676be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 568988323c57bd25a58f05dfa492d9b9c8ab62c5153satok // Unbind service when there is no active clients. 569988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mContext.unbindService(mInternalConnection); 570988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 571988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 5726be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok 5736be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok public void removeAll() { 5746be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Slog.e(TAG, "Remove the spell checker bind unexpectedly."); 575df5659d3d317b5cf351baffe3e0d4876e89678bfsatok synchronized(mSpellCheckerMap) { 576df5659d3d317b5cf351baffe3e0d4876e89678bfsatok mListeners.clear(); 577df5659d3d317b5cf351baffe3e0d4876e89678bfsatok cleanLocked(); 578df5659d3d317b5cf351baffe3e0d4876e89678bfsatok } 5796be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 580988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 581988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 582988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private class InternalServiceConnection implements ServiceConnection { 583988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final ISpellCheckerSessionListener mListener; 584988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final String mSciId; 585988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final String mLocale; 5865357806980269d846a15c845a6fcc0384fb18860satok private final Bundle mBundle; 587988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public InternalServiceConnection( 5885357806980269d846a15c845a6fcc0384fb18860satok String id, String locale, ISpellCheckerSessionListener listener, Bundle bundle) { 589988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSciId = id; 590988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mLocale = locale; 591988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mListener = listener; 5925357806980269d846a15c845a6fcc0384fb18860satok mBundle = bundle; 593988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 594988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 595988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 596988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onServiceConnected(ComponentName name, IBinder service) { 597988323c57bd25a58f05dfa492d9b9c8ab62c5153satok synchronized(mSpellCheckerMap) { 5986be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok if (DBG) { 5996be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok Slog.w(TAG, "onServiceConnected: " + name); 6006be6d7548fb7c29a4d46dc985318ab2adf69f95fsatok } 601988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ISpellCheckerService spellChecker = ISpellCheckerService.Stub.asInterface(service); 602988323c57bd25a58f05dfa492d9b9c8ab62c5153satok final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); 603988323c57bd25a58f05dfa492d9b9c8ab62c5153satok if (group != null) { 604988323c57bd25a58f05dfa492d9b9c8ab62c5153satok group.onServiceConnected(spellChecker); 605988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 606988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 607988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 608988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 609988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 610988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void onServiceDisconnected(ComponentName name) { 611988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mSpellCheckerBindGroups.remove(mSciId); 612988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 613988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 614988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 615988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private class InternalDeathRecipient implements IBinder.DeathRecipient { 616988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final ITextServicesSessionListener mTsListener; 617988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final ISpellCheckerSessionListener mScListener; 618988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public final String mScLocale; 619988323c57bd25a58f05dfa492d9b9c8ab62c5153satok private final SpellCheckerBindGroup mGroup; 620df5659d3d317b5cf351baffe3e0d4876e89678bfsatok public final int mUid; 6215357806980269d846a15c845a6fcc0384fb18860satok public final Bundle mBundle; 622988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public InternalDeathRecipient(SpellCheckerBindGroup group, 623988323c57bd25a58f05dfa492d9b9c8ab62c5153satok ITextServicesSessionListener tsListener, String scLocale, 6245357806980269d846a15c845a6fcc0384fb18860satok ISpellCheckerSessionListener scListener, int uid, Bundle bundle) { 625988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mTsListener = tsListener; 626988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mScListener = scListener; 627988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mScLocale = scLocale; 628988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mGroup = group; 629df5659d3d317b5cf351baffe3e0d4876e89678bfsatok mUid = uid; 6305357806980269d846a15c845a6fcc0384fb18860satok mBundle = bundle; 631988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 632988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 633988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) { 634df5659d3d317b5cf351baffe3e0d4876e89678bfsatok return listener.asBinder().equals(mScListener.asBinder()); 635988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 636988323c57bd25a58f05dfa492d9b9c8ab62c5153satok 637988323c57bd25a58f05dfa492d9b9c8ab62c5153satok @Override 638988323c57bd25a58f05dfa492d9b9c8ab62c5153satok public void binderDied() { 639988323c57bd25a58f05dfa492d9b9c8ab62c5153satok mGroup.removeListener(mScListener); 640988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 641988323c57bd25a58f05dfa492d9b9c8ab62c5153satok } 642988323c57bd25a58f05dfa492d9b9c8ab62c5153satok} 643