diff options
Diffstat (limited to 'services/java/com/android/server/TextServicesManagerService.java')
-rw-r--r-- | services/java/com/android/server/TextServicesManagerService.java | 983 |
1 files changed, 0 insertions, 983 deletions
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java deleted file mode 100644 index 0964767..0000000 --- a/services/java/com/android/server/TextServicesManagerService.java +++ /dev/null @@ -1,983 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.server; - -import com.android.internal.content.PackageMonitor; -import com.android.internal.textservice.ISpellCheckerService; -import com.android.internal.textservice.ISpellCheckerSession; -import com.android.internal.textservice.ISpellCheckerSessionListener; -import com.android.internal.textservice.ITextServicesManager; -import com.android.internal.textservice.ITextServicesSessionListener; - -import org.xmlpull.v1.XmlPullParserException; - -import android.app.ActivityManagerNative; -import android.app.AppGlobals; -import android.app.IUserSwitchObserver; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.pm.IPackageManager; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.content.pm.ServiceInfo; -import android.os.Binder; -import android.os.Bundle; -import android.os.IBinder; -import android.os.IRemoteCallback; -import android.os.Process; -import android.os.RemoteException; -import android.os.UserHandle; -import android.provider.Settings; -import android.service.textservice.SpellCheckerService; -import android.text.TextUtils; -import android.util.Slog; -import android.view.inputmethod.InputMethodManager; -import android.view.inputmethod.InputMethodSubtype; -import android.view.textservice.SpellCheckerInfo; -import android.view.textservice.SpellCheckerSubtype; - -import java.io.FileDescriptor; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.CopyOnWriteArrayList; - -public class TextServicesManagerService extends ITextServicesManager.Stub { - private static final String TAG = TextServicesManagerService.class.getSimpleName(); - private static final boolean DBG = false; - - private final Context mContext; - private boolean mSystemReady; - private final TextServicesMonitor mMonitor; - private final HashMap<String, SpellCheckerInfo> mSpellCheckerMap = - new HashMap<String, SpellCheckerInfo>(); - private final ArrayList<SpellCheckerInfo> mSpellCheckerList = new ArrayList<SpellCheckerInfo>(); - private final HashMap<String, SpellCheckerBindGroup> mSpellCheckerBindGroups = - new HashMap<String, SpellCheckerBindGroup>(); - private final TextServicesSettings mSettings; - - public void systemRunning() { - if (!mSystemReady) { - mSystemReady = true; - } - } - - public TextServicesManagerService(Context context) { - mSystemReady = false; - mContext = context; - int userId = UserHandle.USER_OWNER; - try { - ActivityManagerNative.getDefault().registerUserSwitchObserver( - new IUserSwitchObserver.Stub() { - @Override - public void onUserSwitching(int newUserId, IRemoteCallback reply) { - synchronized(mSpellCheckerMap) { - switchUserLocked(newUserId); - } - if (reply != null) { - try { - reply.sendResult(null); - } catch (RemoteException e) { - } - } - } - - @Override - public void onUserSwitchComplete(int newUserId) throws RemoteException { - } - }); - userId = ActivityManagerNative.getDefault().getCurrentUser().id; - } catch (RemoteException e) { - Slog.w(TAG, "Couldn't get current user ID; guessing it's 0", e); - } - mMonitor = new TextServicesMonitor(); - mMonitor.register(context, null, true); - mSettings = new TextServicesSettings(context.getContentResolver(), userId); - - // "switchUserLocked" initializes the states for the foreground user - switchUserLocked(userId); - } - - private void switchUserLocked(int userId) { - mSettings.setCurrentUserId(userId); - unbindServiceLocked(); - buildSpellCheckerMapLocked(mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); - SpellCheckerInfo sci = getCurrentSpellChecker(null); - if (sci == null) { - sci = findAvailSpellCheckerLocked(null, null); - if (sci != null) { - // Set the current spell checker if there is one or more spell checkers - // available. In this case, "sci" is the first one in the available spell - // checkers. - setCurrentSpellCheckerLocked(sci.getId()); - } - } - } - - private class TextServicesMonitor extends PackageMonitor { - private boolean isChangingPackagesOfCurrentUser() { - final int userId = getChangingUserId(); - final boolean retval = userId == mSettings.getCurrentUserId(); - if (DBG) { - Slog.d(TAG, "--- ignore this call back from a background user: " + userId); - } - return retval; - } - - @Override - public void onSomePackagesChanged() { - if (!isChangingPackagesOfCurrentUser()) { - return; - } - synchronized (mSpellCheckerMap) { - buildSpellCheckerMapLocked( - mContext, mSpellCheckerList, mSpellCheckerMap, mSettings); - // TODO: Update for each locale - SpellCheckerInfo sci = getCurrentSpellChecker(null); - // If no spell checker is enabled, just return. The user should explicitly - // enable the spell checker. - if (sci == null) return; - final String packageName = sci.getPackageName(); - final int change = isPackageDisappearing(packageName); - if (// Package disappearing - change == PACKAGE_PERMANENT_CHANGE || change == PACKAGE_TEMPORARY_CHANGE - // Package modified - || isPackageModified(packageName)) { - sci = findAvailSpellCheckerLocked(null, packageName); - if (sci != null) { - setCurrentSpellCheckerLocked(sci.getId()); - } - } - } - } - } - - private static void buildSpellCheckerMapLocked(Context context, - ArrayList<SpellCheckerInfo> list, HashMap<String, SpellCheckerInfo> map, - TextServicesSettings settings) { - list.clear(); - map.clear(); - final PackageManager pm = context.getPackageManager(); - final List<ResolveInfo> services = pm.queryIntentServicesAsUser( - new Intent(SpellCheckerService.SERVICE_INTERFACE), PackageManager.GET_META_DATA, - settings.getCurrentUserId()); - final int N = services.size(); - for (int i = 0; i < N; ++i) { - final ResolveInfo ri = services.get(i); - final ServiceInfo si = ri.serviceInfo; - final ComponentName compName = new ComponentName(si.packageName, si.name); - if (!android.Manifest.permission.BIND_TEXT_SERVICE.equals(si.permission)) { - Slog.w(TAG, "Skipping text service " + compName - + ": it does not require the permission " - + android.Manifest.permission.BIND_TEXT_SERVICE); - continue; - } - if (DBG) Slog.d(TAG, "Add: " + compName); - try { - final SpellCheckerInfo sci = new SpellCheckerInfo(context, ri); - if (sci.getSubtypeCount() <= 0) { - Slog.w(TAG, "Skipping text service " + compName - + ": it does not contain subtypes."); - continue; - } - list.add(sci); - map.put(sci.getId(), sci); - } catch (XmlPullParserException e) { - Slog.w(TAG, "Unable to load the spell checker " + compName, e); - } catch (IOException e) { - Slog.w(TAG, "Unable to load the spell checker " + compName, e); - } - } - if (DBG) { - Slog.d(TAG, "buildSpellCheckerMapLocked: " + list.size() + "," + map.size()); - } - } - - // --------------------------------------------------------------------------------------- - // Check whether or not this is a valid IPC. Assumes an IPC is valid when either - // 1) it comes from the system process - // 2) the calling process' user id is identical to the current user id TSMS thinks. - private boolean calledFromValidUser() { - final int uid = Binder.getCallingUid(); - final int userId = UserHandle.getUserId(uid); - if (DBG) { - Slog.d(TAG, "--- calledFromForegroundUserOrSystemProcess ? " - + "calling uid = " + uid + " system uid = " + Process.SYSTEM_UID - + " calling userId = " + userId + ", foreground user id = " - + mSettings.getCurrentUserId()); - try { - final String[] packageNames = AppGlobals.getPackageManager().getPackagesForUid(uid); - for (int i = 0; i < packageNames.length; ++i) { - if (DBG) { - Slog.d(TAG, "--- process name for "+ uid + " = " + packageNames[i]); - } - } - } catch (RemoteException e) { - } - } - - if (uid == Process.SYSTEM_UID || userId == mSettings.getCurrentUserId()) { - return true; - } else { - Slog.w(TAG, "--- IPC called from background users. Ignore. \n" + getStackTrace()); - return false; - } - } - - private boolean bindCurrentSpellCheckerService( - Intent service, ServiceConnection conn, int flags) { - if (service == null || conn == null) { - Slog.e(TAG, "--- bind failed: service = " + service + ", conn = " + conn); - return false; - } - return mContext.bindServiceAsUser(service, conn, flags, - new UserHandle(mSettings.getCurrentUserId())); - } - - private void unbindServiceLocked() { - for (SpellCheckerBindGroup scbg : mSpellCheckerBindGroups.values()) { - scbg.removeAll(); - } - mSpellCheckerBindGroups.clear(); - } - - // TODO: find an appropriate spell checker for specified locale - private SpellCheckerInfo findAvailSpellCheckerLocked(String locale, String prefPackage) { - final int spellCheckersCount = mSpellCheckerList.size(); - if (spellCheckersCount == 0) { - Slog.w(TAG, "no available spell checker services found"); - return null; - } - if (prefPackage != null) { - for (int i = 0; i < spellCheckersCount; ++i) { - final SpellCheckerInfo sci = mSpellCheckerList.get(i); - if (prefPackage.equals(sci.getPackageName())) { - if (DBG) { - Slog.d(TAG, "findAvailSpellCheckerLocked: " + sci.getPackageName()); - } - return sci; - } - } - } - if (spellCheckersCount > 1) { - Slog.w(TAG, "more than one spell checker service found, picking first"); - } - return mSpellCheckerList.get(0); - } - - // TODO: Save SpellCheckerService by supported languages. Currently only one spell - // checker is saved. - @Override - public SpellCheckerInfo getCurrentSpellChecker(String locale) { - // TODO: Make this work even for non-current users? - if (!calledFromValidUser()) { - return null; - } - synchronized (mSpellCheckerMap) { - final String curSpellCheckerId = mSettings.getSelectedSpellChecker(); - if (DBG) { - Slog.w(TAG, "getCurrentSpellChecker: " + curSpellCheckerId); - } - if (TextUtils.isEmpty(curSpellCheckerId)) { - return null; - } - return mSpellCheckerMap.get(curSpellCheckerId); - } - } - - // TODO: Respect allowImplicitlySelectedSubtype - // TODO: Save SpellCheckerSubtype by supported languages by looking at "locale". - @Override - public SpellCheckerSubtype getCurrentSpellCheckerSubtype( - String locale, boolean allowImplicitlySelectedSubtype) { - // TODO: Make this work even for non-current users? - if (!calledFromValidUser()) { - return null; - } - synchronized (mSpellCheckerMap) { - final String subtypeHashCodeStr = mSettings.getSelectedSpellCheckerSubtype(); - if (DBG) { - Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCodeStr); - } - final SpellCheckerInfo sci = getCurrentSpellChecker(null); - if (sci == null || sci.getSubtypeCount() == 0) { - if (DBG) { - Slog.w(TAG, "Subtype not found."); - } - return null; - } - final int hashCode; - if (!TextUtils.isEmpty(subtypeHashCodeStr)) { - hashCode = Integer.valueOf(subtypeHashCodeStr); - } else { - hashCode = 0; - } - if (hashCode == 0 && !allowImplicitlySelectedSubtype) { - return null; - } - String candidateLocale = null; - if (hashCode == 0) { - // Spell checker language settings == "auto" - final InputMethodManager imm = - (InputMethodManager)mContext.getSystemService(Context.INPUT_METHOD_SERVICE); - if (imm != null) { - final InputMethodSubtype currentInputMethodSubtype = - imm.getCurrentInputMethodSubtype(); - if (currentInputMethodSubtype != null) { - final String localeString = currentInputMethodSubtype.getLocale(); - if (!TextUtils.isEmpty(localeString)) { - // 1. Use keyboard locale if available in the spell checker - candidateLocale = localeString; - } - } - } - if (candidateLocale == null) { - // 2. Use System locale if available in the spell checker - candidateLocale = mContext.getResources().getConfiguration().locale.toString(); - } - } - SpellCheckerSubtype candidate = null; - for (int i = 0; i < sci.getSubtypeCount(); ++i) { - final SpellCheckerSubtype scs = sci.getSubtypeAt(i); - if (hashCode == 0) { - final String scsLocale = scs.getLocale(); - if (candidateLocale.equals(scsLocale)) { - return scs; - } else if (candidate == null) { - if (candidateLocale.length() >= 2 && scsLocale.length() >= 2 - && candidateLocale.startsWith(scsLocale)) { - // Fall back to the applicable language - candidate = scs; - } - } - } else if (scs.hashCode() == hashCode) { - if (DBG) { - Slog.w(TAG, "Return subtype " + scs.hashCode() + ", input= " + locale - + ", " + scs.getLocale()); - } - // 3. Use the user specified spell check language - return scs; - } - } - // 4. Fall back to the applicable language and return it if not null - // 5. Simply just return it even if it's null which means we could find no suitable - // spell check languages - return candidate; - } - } - - @Override - public void getSpellCheckerService(String sciId, String locale, - ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener, - Bundle bundle) { - if (!calledFromValidUser()) { - return; - } - if (!mSystemReady) { - return; - } - if (TextUtils.isEmpty(sciId) || tsListener == null || scListener == null) { - Slog.e(TAG, "getSpellCheckerService: Invalid input."); - return; - } - synchronized(mSpellCheckerMap) { - if (!mSpellCheckerMap.containsKey(sciId)) { - return; - } - final SpellCheckerInfo sci = mSpellCheckerMap.get(sciId); - final int uid = Binder.getCallingUid(); - if (mSpellCheckerBindGroups.containsKey(sciId)) { - final SpellCheckerBindGroup bindGroup = mSpellCheckerBindGroups.get(sciId); - if (bindGroup != null) { - final InternalDeathRecipient recipient = - mSpellCheckerBindGroups.get(sciId).addListener( - tsListener, locale, scListener, uid, bundle); - if (recipient == null) { - if (DBG) { - Slog.w(TAG, "Didn't create a death recipient."); - } - return; - } - if (bindGroup.mSpellChecker == null & bindGroup.mConnected) { - Slog.e(TAG, "The state of the spell checker bind group is illegal."); - bindGroup.removeAll(); - } else if (bindGroup.mSpellChecker != null) { - if (DBG) { - Slog.w(TAG, "Existing bind found. Return a spell checker session now. " - + "Listeners count = " + bindGroup.mListeners.size()); - } - try { - final ISpellCheckerSession session = - bindGroup.mSpellChecker.getISpellCheckerSession( - recipient.mScLocale, recipient.mScListener, bundle); - if (session != null) { - tsListener.onServiceConnected(session); - return; - } else { - if (DBG) { - Slog.w(TAG, "Existing bind already expired. "); - } - bindGroup.removeAll(); - } - } catch (RemoteException e) { - Slog.e(TAG, "Exception in getting spell checker session: " + e); - bindGroup.removeAll(); - } - } - } - } - final long ident = Binder.clearCallingIdentity(); - try { - startSpellCheckerServiceInnerLocked( - sci, locale, tsListener, scListener, uid, bundle); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - return; - } - - @Override - public boolean isSpellCheckerEnabled() { - if (!calledFromValidUser()) { - return false; - } - synchronized(mSpellCheckerMap) { - return isSpellCheckerEnabledLocked(); - } - } - - private void startSpellCheckerServiceInnerLocked(SpellCheckerInfo info, String locale, - ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener, - int uid, Bundle bundle) { - if (DBG) { - Slog.w(TAG, "Start spell checker session inner locked."); - } - final String sciId = info.getId(); - final InternalServiceConnection connection = new InternalServiceConnection( - sciId, locale, bundle); - final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE); - serviceIntent.setComponent(info.getComponent()); - if (DBG) { - Slog.w(TAG, "bind service: " + info.getId()); - } - if (!bindCurrentSpellCheckerService(serviceIntent, connection, Context.BIND_AUTO_CREATE)) { - Slog.e(TAG, "Failed to get a spell checker service."); - return; - } - final SpellCheckerBindGroup group = new SpellCheckerBindGroup( - connection, tsListener, locale, scListener, uid, bundle); - mSpellCheckerBindGroups.put(sciId, group); - } - - @Override - public SpellCheckerInfo[] getEnabledSpellCheckers() { - // TODO: Make this work even for non-current users? - if (!calledFromValidUser()) { - return null; - } - if (DBG) { - Slog.d(TAG, "getEnabledSpellCheckers: " + mSpellCheckerList.size()); - for (int i = 0; i < mSpellCheckerList.size(); ++i) { - Slog.d(TAG, "EnabledSpellCheckers: " + mSpellCheckerList.get(i).getPackageName()); - } - } - return mSpellCheckerList.toArray(new SpellCheckerInfo[mSpellCheckerList.size()]); - } - - @Override - public void finishSpellCheckerService(ISpellCheckerSessionListener listener) { - if (!calledFromValidUser()) { - return; - } - if (DBG) { - Slog.d(TAG, "FinishSpellCheckerService"); - } - synchronized(mSpellCheckerMap) { - final ArrayList<SpellCheckerBindGroup> removeList = - new ArrayList<SpellCheckerBindGroup>(); - for (SpellCheckerBindGroup group : mSpellCheckerBindGroups.values()) { - if (group == null) continue; - // Use removeList to avoid modifying mSpellCheckerBindGroups in this loop. - removeList.add(group); - } - final int removeSize = removeList.size(); - for (int i = 0; i < removeSize; ++i) { - removeList.get(i).removeListener(listener); - } - } - } - - @Override - public void setCurrentSpellChecker(String locale, String sciId) { - if (!calledFromValidUser()) { - return; - } - synchronized(mSpellCheckerMap) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Requires permission " - + android.Manifest.permission.WRITE_SECURE_SETTINGS); - } - setCurrentSpellCheckerLocked(sciId); - } - } - - @Override - public void setCurrentSpellCheckerSubtype(String locale, int hashCode) { - if (!calledFromValidUser()) { - return; - } - synchronized(mSpellCheckerMap) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Requires permission " - + android.Manifest.permission.WRITE_SECURE_SETTINGS); - } - setCurrentSpellCheckerSubtypeLocked(hashCode); - } - } - - @Override - public void setSpellCheckerEnabled(boolean enabled) { - if (!calledFromValidUser()) { - return; - } - synchronized(mSpellCheckerMap) { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.WRITE_SECURE_SETTINGS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException( - "Requires permission " - + android.Manifest.permission.WRITE_SECURE_SETTINGS); - } - setSpellCheckerEnabledLocked(enabled); - } - } - - private void setCurrentSpellCheckerLocked(String sciId) { - if (DBG) { - Slog.w(TAG, "setCurrentSpellChecker: " + sciId); - } - if (TextUtils.isEmpty(sciId) || !mSpellCheckerMap.containsKey(sciId)) return; - final SpellCheckerInfo currentSci = getCurrentSpellChecker(null); - if (currentSci != null && currentSci.getId().equals(sciId)) { - // Do nothing if the current spell checker is same as new spell checker. - return; - } - final long ident = Binder.clearCallingIdentity(); - try { - mSettings.putSelectedSpellChecker(sciId); - setCurrentSpellCheckerSubtypeLocked(0); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private void setCurrentSpellCheckerSubtypeLocked(int hashCode) { - if (DBG) { - Slog.w(TAG, "setCurrentSpellCheckerSubtype: " + hashCode); - } - final SpellCheckerInfo sci = getCurrentSpellChecker(null); - int tempHashCode = 0; - for (int i = 0; sci != null && i < sci.getSubtypeCount(); ++i) { - if(sci.getSubtypeAt(i).hashCode() == hashCode) { - tempHashCode = hashCode; - break; - } - } - final long ident = Binder.clearCallingIdentity(); - try { - mSettings.putSelectedSpellCheckerSubtype(tempHashCode); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private void setSpellCheckerEnabledLocked(boolean enabled) { - if (DBG) { - Slog.w(TAG, "setSpellCheckerEnabled: " + enabled); - } - final long ident = Binder.clearCallingIdentity(); - try { - mSettings.setSpellCheckerEnabled(enabled); - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - private boolean isSpellCheckerEnabledLocked() { - final long ident = Binder.clearCallingIdentity(); - try { - final boolean retval = mSettings.isSpellCheckerEnabled(); - if (DBG) { - Slog.w(TAG, "getSpellCheckerEnabled: " + retval); - } - return retval; - } finally { - Binder.restoreCallingIdentity(ident); - } - } - - @Override - protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { - if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) - != PackageManager.PERMISSION_GRANTED) { - - pw.println("Permission Denial: can't dump TextServicesManagerService from from pid=" - + Binder.getCallingPid() - + ", uid=" + Binder.getCallingUid()); - return; - } - - synchronized(mSpellCheckerMap) { - pw.println("Current Text Services Manager state:"); - pw.println(" Spell Checker Map:"); - for (Map.Entry<String, SpellCheckerInfo> ent : mSpellCheckerMap.entrySet()) { - pw.print(" "); pw.print(ent.getKey()); pw.println(":"); - SpellCheckerInfo info = ent.getValue(); - pw.print(" "); pw.print("id="); pw.println(info.getId()); - pw.print(" "); pw.print("comp="); - pw.println(info.getComponent().toShortString()); - int NS = info.getSubtypeCount(); - for (int i=0; i<NS; i++) { - SpellCheckerSubtype st = info.getSubtypeAt(i); - pw.print(" "); pw.print("Subtype #"); pw.print(i); pw.println(":"); - pw.print(" "); pw.print("locale="); pw.println(st.getLocale()); - pw.print(" "); pw.print("extraValue="); - pw.println(st.getExtraValue()); - } - } - pw.println(""); - pw.println(" Spell Checker Bind Groups:"); - for (Map.Entry<String, SpellCheckerBindGroup> ent - : mSpellCheckerBindGroups.entrySet()) { - SpellCheckerBindGroup grp = ent.getValue(); - pw.print(" "); pw.print(ent.getKey()); pw.print(" "); - pw.print(grp); pw.println(":"); - pw.print(" "); pw.print("mInternalConnection="); - pw.println(grp.mInternalConnection); - pw.print(" "); pw.print("mSpellChecker="); - pw.println(grp.mSpellChecker); - pw.print(" "); pw.print("mBound="); pw.print(grp.mBound); - pw.print(" mConnected="); pw.println(grp.mConnected); - int NL = grp.mListeners.size(); - for (int i=0; i<NL; i++) { - InternalDeathRecipient listener = grp.mListeners.get(i); - pw.print(" "); pw.print("Listener #"); pw.print(i); pw.println(":"); - pw.print(" "); pw.print("mTsListener="); - pw.println(listener.mTsListener); - pw.print(" "); pw.print("mScListener="); - pw.println(listener.mScListener); - pw.print(" "); pw.print("mGroup="); - pw.println(listener.mGroup); - pw.print(" "); pw.print("mScLocale="); - pw.print(listener.mScLocale); - pw.print(" mUid="); pw.println(listener.mUid); - } - } - } - } - - // SpellCheckerBindGroup contains active text service session listeners. - // If there are no listeners anymore, the SpellCheckerBindGroup instance will be removed from - // mSpellCheckerBindGroups - private class SpellCheckerBindGroup { - private final String TAG = SpellCheckerBindGroup.class.getSimpleName(); - private final InternalServiceConnection mInternalConnection; - private final CopyOnWriteArrayList<InternalDeathRecipient> mListeners = - new CopyOnWriteArrayList<InternalDeathRecipient>(); - public boolean mBound; - public ISpellCheckerService mSpellChecker; - public boolean mConnected; - - public SpellCheckerBindGroup(InternalServiceConnection connection, - ITextServicesSessionListener listener, String locale, - ISpellCheckerSessionListener scListener, int uid, Bundle bundle) { - mInternalConnection = connection; - mBound = true; - mConnected = false; - addListener(listener, locale, scListener, uid, bundle); - } - - public void onServiceConnected(ISpellCheckerService spellChecker) { - if (DBG) { - Slog.d(TAG, "onServiceConnected"); - } - - for (InternalDeathRecipient listener : mListeners) { - try { - final ISpellCheckerSession session = spellChecker.getISpellCheckerSession( - listener.mScLocale, listener.mScListener, listener.mBundle); - synchronized(mSpellCheckerMap) { - if (mListeners.contains(listener)) { - listener.mTsListener.onServiceConnected(session); - } - } - } catch (RemoteException e) { - Slog.e(TAG, "Exception in getting the spell checker session." - + "Reconnect to the spellchecker. ", e); - removeAll(); - return; - } - } - synchronized(mSpellCheckerMap) { - mSpellChecker = spellChecker; - mConnected = true; - } - } - - public InternalDeathRecipient addListener(ITextServicesSessionListener tsListener, - String locale, ISpellCheckerSessionListener scListener, int uid, Bundle bundle) { - if (DBG) { - Slog.d(TAG, "addListener: " + locale); - } - InternalDeathRecipient recipient = null; - synchronized(mSpellCheckerMap) { - try { - final int size = mListeners.size(); - for (int i = 0; i < size; ++i) { - if (mListeners.get(i).hasSpellCheckerListener(scListener)) { - // do not add the lister if the group already contains this. - return null; - } - } - recipient = new InternalDeathRecipient( - this, tsListener, locale, scListener, uid, bundle); - scListener.asBinder().linkToDeath(recipient, 0); - mListeners.add(recipient); - } catch(RemoteException e) { - // do nothing - } - cleanLocked(); - } - return recipient; - } - - public void removeListener(ISpellCheckerSessionListener listener) { - if (DBG) { - Slog.w(TAG, "remove listener: " + listener.hashCode()); - } - synchronized(mSpellCheckerMap) { - final int size = mListeners.size(); - final ArrayList<InternalDeathRecipient> removeList = - new ArrayList<InternalDeathRecipient>(); - for (int i = 0; i < size; ++i) { - final InternalDeathRecipient tempRecipient = mListeners.get(i); - if(tempRecipient.hasSpellCheckerListener(listener)) { - if (DBG) { - Slog.w(TAG, "found existing listener."); - } - removeList.add(tempRecipient); - } - } - final int removeSize = removeList.size(); - for (int i = 0; i < removeSize; ++i) { - if (DBG) { - Slog.w(TAG, "Remove " + removeList.get(i)); - } - final InternalDeathRecipient idr = removeList.get(i); - idr.mScListener.asBinder().unlinkToDeath(idr, 0); - mListeners.remove(idr); - } - cleanLocked(); - } - } - - // cleanLocked may remove elements from mSpellCheckerBindGroups - private void cleanLocked() { - if (DBG) { - Slog.d(TAG, "cleanLocked"); - } - // If there are no more active listeners, clean up. Only do this - // once. - if (mBound && mListeners.isEmpty()) { - mBound = false; - final String sciId = mInternalConnection.mSciId; - SpellCheckerBindGroup cur = mSpellCheckerBindGroups.get(sciId); - if (cur == this) { - if (DBG) { - Slog.d(TAG, "Remove bind group."); - } - mSpellCheckerBindGroups.remove(sciId); - } - mContext.unbindService(mInternalConnection); - } - } - - public void removeAll() { - Slog.e(TAG, "Remove the spell checker bind unexpectedly."); - synchronized(mSpellCheckerMap) { - final int size = mListeners.size(); - for (int i = 0; i < size; ++i) { - final InternalDeathRecipient idr = mListeners.get(i); - idr.mScListener.asBinder().unlinkToDeath(idr, 0); - } - mListeners.clear(); - cleanLocked(); - } - } - } - - private class InternalServiceConnection implements ServiceConnection { - private final String mSciId; - private final String mLocale; - private final Bundle mBundle; - public InternalServiceConnection( - String id, String locale, Bundle bundle) { - mSciId = id; - mLocale = locale; - mBundle = bundle; - } - - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - synchronized(mSpellCheckerMap) { - onServiceConnectedInnerLocked(name, service); - } - } - - private void onServiceConnectedInnerLocked(ComponentName name, IBinder service) { - if (DBG) { - Slog.w(TAG, "onServiceConnected: " + name); - } - final ISpellCheckerService spellChecker = - ISpellCheckerService.Stub.asInterface(service); - final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); - if (group != null && this == group.mInternalConnection) { - group.onServiceConnected(spellChecker); - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - synchronized(mSpellCheckerMap) { - final SpellCheckerBindGroup group = mSpellCheckerBindGroups.get(mSciId); - if (group != null && this == group.mInternalConnection) { - mSpellCheckerBindGroups.remove(mSciId); - } - } - } - } - - private class InternalDeathRecipient implements IBinder.DeathRecipient { - public final ITextServicesSessionListener mTsListener; - public final ISpellCheckerSessionListener mScListener; - public final String mScLocale; - private final SpellCheckerBindGroup mGroup; - public final int mUid; - public final Bundle mBundle; - public InternalDeathRecipient(SpellCheckerBindGroup group, - ITextServicesSessionListener tsListener, String scLocale, - ISpellCheckerSessionListener scListener, int uid, Bundle bundle) { - mTsListener = tsListener; - mScListener = scListener; - mScLocale = scLocale; - mGroup = group; - mUid = uid; - mBundle = bundle; - } - - public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) { - return listener.asBinder().equals(mScListener.asBinder()); - } - - @Override - public void binderDied() { - mGroup.removeListener(mScListener); - } - } - - private static class TextServicesSettings { - private final ContentResolver mResolver; - private int mCurrentUserId; - public TextServicesSettings(ContentResolver resolver, int userId) { - mResolver = resolver; - mCurrentUserId = userId; - } - - public void setCurrentUserId(int userId) { - if (DBG) { - Slog.d(TAG, "--- Swtich the current user from " + mCurrentUserId + " to " - + userId + ", new ime = " + getSelectedSpellChecker()); - } - // TSMS settings are kept per user, so keep track of current user - mCurrentUserId = userId; - } - - public int getCurrentUserId() { - return mCurrentUserId; - } - - public void putSelectedSpellChecker(String sciId) { - Settings.Secure.putStringForUser(mResolver, - Settings.Secure.SELECTED_SPELL_CHECKER, sciId, mCurrentUserId); - } - - public void putSelectedSpellCheckerSubtype(int hashCode) { - Settings.Secure.putStringForUser(mResolver, - Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, String.valueOf(hashCode), - mCurrentUserId); - } - - public void setSpellCheckerEnabled(boolean enabled) { - Settings.Secure.putIntForUser(mResolver, - Settings.Secure.SPELL_CHECKER_ENABLED, enabled ? 1 : 0, mCurrentUserId); - } - - public String getSelectedSpellChecker() { - return Settings.Secure.getStringForUser(mResolver, - Settings.Secure.SELECTED_SPELL_CHECKER, mCurrentUserId); - } - - public String getSelectedSpellCheckerSubtype() { - return Settings.Secure.getStringForUser(mResolver, - Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE, mCurrentUserId); - } - - public boolean isSpellCheckerEnabled() { - return Settings.Secure.getIntForUser(mResolver, - Settings.Secure.SPELL_CHECKER_ENABLED, 1, mCurrentUserId) == 1; - } - } - - // ---------------------------------------------------------------------- - // Utilities for debug - private static String getStackTrace() { - final StringBuilder sb = new StringBuilder(); - try { - throw new RuntimeException(); - } catch (RuntimeException e) { - final StackTraceElement[] frames = e.getStackTrace(); - // Start at 1 because the first frame is here and we don't care about it - for (int j = 1; j < frames.length; ++j) { - sb.append(frames[j].toString() + "\n"); - } - } - return sb.toString(); - } -} |