Merge "API cleanup for the spell checker framework"
diff --git a/api/current.txt b/api/current.txt
index bdc695f..035da89 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -18185,13 +18185,21 @@
 
   public abstract class SpellCheckerService extends android.app.Service {
     ctor public SpellCheckerService();
-    method public void cancel();
-    method public abstract android.view.textservice.SuggestionsInfo getSuggestions(android.view.textservice.TextInfo, int, java.lang.String);
-    method public android.view.textservice.SuggestionsInfo[] getSuggestionsMultiple(android.view.textservice.TextInfo[], java.lang.String, int, boolean);
+    method public abstract android.service.textservice.SpellCheckerService.Session createSession();
     method public final android.os.IBinder onBind(android.content.Intent);
     field public static final java.lang.String SERVICE_INTERFACE = "android.service.textservice.SpellCheckerService";
   }
 
+  public abstract class SpellCheckerService.Session {
+    ctor public SpellCheckerService.Session();
+    method public android.os.Bundle getBundle();
+    method public java.lang.String getLocale();
+    method public void onCancel();
+    method public abstract void onCreate();
+    method public abstract android.view.textservice.SuggestionsInfo onGetSuggestions(android.view.textservice.TextInfo, int);
+    method public android.view.textservice.SuggestionsInfo[] onGetSuggestionsMultiple(android.view.textservice.TextInfo[], int, boolean);
+  }
+
 }
 
 package android.service.wallpaper {
@@ -24308,7 +24316,7 @@
   }
 
   public final class TextServicesManager {
-    method public android.view.textservice.SpellCheckerSession newSpellCheckerSession(java.util.Locale, android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
+    method public android.view.textservice.SpellCheckerSession newSpellCheckerSession(android.os.Bundle, java.util.Locale, android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener, boolean);
   }
 
 }
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 6f70ab8..3e2e38e 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -22,6 +22,7 @@
 
 import android.app.Service;
 import android.content.Intent;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.util.Log;
@@ -43,45 +44,6 @@
 
     private final SpellCheckerServiceBinder mBinder = new SpellCheckerServiceBinder(this);
 
-    /**
-     * Get suggestions for specified text in TextInfo.
-     * This function will run on the incoming IPC thread. So, this is not called on the main thread,
-     * but will be called in series on another thread.
-     * @param textInfo the text metadata
-     * @param suggestionsLimit the number of limit of suggestions returned
-     * @param locale the locale for getting suggestions
-     * @return SuggestionInfo which contains suggestions for textInfo
-     */
-    public abstract SuggestionsInfo getSuggestions(
-            TextInfo textInfo, int suggestionsLimit, String locale);
-
-    /**
-     * A batch process of onGetSuggestions.
-     * This function will run on the incoming IPC thread. So, this is not called on the main thread,
-     * but will be called in series on another thread.
-     * @param textInfos an array of the text metadata
-     * @param locale the locale for getting suggestions
-     * @param suggestionsLimit the number of limit of suggestions returned
-     * @param sequentialWords true if textInfos can be treated as sequential words.
-     * @return an array of SuggestionInfo of onGetSuggestions
-     */
-    public SuggestionsInfo[] getSuggestionsMultiple(
-            TextInfo[] textInfos, String locale, int suggestionsLimit, boolean sequentialWords) {
-        final int length = textInfos.length;
-        final SuggestionsInfo[] retval = new SuggestionsInfo[length];
-        for (int i = 0; i < length; ++i) {
-            retval[i] = getSuggestions(textInfos[i], suggestionsLimit, locale);
-            retval[i].setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence());
-        }
-        return retval;
-    }
-
-    /**
-     * Request to abort all tasks executed in SpellChecker.
-     * This function will run on the incoming IPC thread. So, this is not called on the main thread,
-     * but will be called in series on another thread.
-     */
-    public void cancel() {}
 
     /**
      * Implement to return the implementation of the internal spell checker
@@ -95,36 +57,125 @@
         return mBinder;
     }
 
-    private static class SpellCheckerSessionImpl extends ISpellCheckerSession.Stub {
-        private final WeakReference<SpellCheckerService> mInternalServiceRef;
-        private final String mLocale;
-        private final ISpellCheckerSessionListener mListener;
+    /**
+     * Factory method to create a spell checker session impl
+     * @return SpellCheckerSessionImpl which should be overridden by a concrete implementation.
+     */
+    public abstract Session createSession();
 
-        public SpellCheckerSessionImpl(
-                SpellCheckerService service, String locale, ISpellCheckerSessionListener listener) {
-            mInternalServiceRef = new WeakReference<SpellCheckerService>(service);
-            mLocale = locale;
+    /**
+     * This abstract class should be overridden by a concrete implementation of a spell checker.
+     */
+    public abstract class Session {
+        private InternalISpellCheckerSession mInternalSession;
+
+        /**
+         * @hide
+         */
+        public final void setInternalISpellCheckerSession(InternalISpellCheckerSession session) {
+            mInternalSession = session;
+        }
+
+        /**
+         * This is called after the class is initialized, at which point it knows it can call
+         * getLocale() etc...
+         */
+        public abstract void onCreate();
+
+        /**
+         * Get suggestions for specified text in TextInfo.
+         * This function will run on the incoming IPC thread.
+         * So, this is not called on the main thread,
+         * but will be called in series on another thread.
+         * @param textInfo the text metadata
+         * @param suggestionsLimit the number of limit of suggestions returned
+         * @return SuggestionInfo which contains suggestions for textInfo
+         */
+        public abstract SuggestionsInfo onGetSuggestions(TextInfo textInfo, int suggestionsLimit);
+
+        /**
+         * A batch process of onGetSuggestions.
+         * This function will run on the incoming IPC thread.
+         * So, this is not called on the main thread,
+         * but will be called in series on another thread.
+         * @param textInfos an array of the text metadata
+         * @param suggestionsLimit the number of limit of suggestions returned
+         * @param sequentialWords true if textInfos can be treated as sequential words.
+         * @return an array of SuggestionInfo of onGetSuggestions
+         */
+        public SuggestionsInfo[] onGetSuggestionsMultiple(TextInfo[] textInfos,
+                int suggestionsLimit, boolean sequentialWords) {
+            final int length = textInfos.length;
+            final SuggestionsInfo[] retval = new SuggestionsInfo[length];
+            for (int i = 0; i < length; ++i) {
+                retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
+                retval[i].setCookieAndSequence(
+                        textInfos[i].getCookie(), textInfos[i].getSequence());
+            }
+            return retval;
+        }
+
+        /**
+         * Request to abort all tasks executed in SpellChecker.
+         * This function will run on the incoming IPC thread.
+         * So, this is not called on the main thread,
+         * but will be called in series on another thread.
+         */
+        public void onCancel() {}
+
+        /**
+         * @return Locale for this session
+         */
+        public String getLocale() {
+            return mInternalSession.getLocale();
+        }
+
+        /**
+         * @return Bundle for this session
+         */
+        public Bundle getBundle() {
+            return mInternalSession.getBundle();
+        }
+    }
+
+    // Preventing from exposing ISpellCheckerSession.aidl, create an internal class.
+    private static class InternalISpellCheckerSession extends ISpellCheckerSession.Stub {
+        private final ISpellCheckerSessionListener mListener;
+        private final Session mSession;
+        private final String mLocale;
+        private final Bundle mBundle;
+
+        public InternalISpellCheckerSession(String locale, ISpellCheckerSessionListener listener,
+                Bundle bundle, Session session) {
             mListener = listener;
+            mSession = session;
+            mLocale = locale;
+            mBundle = bundle;
+            session.setInternalISpellCheckerSession(this);
         }
 
         @Override
-        public void getSuggestionsMultiple(
+        public void onGetSuggestionsMultiple(
                 TextInfo[] textInfos, int suggestionsLimit, boolean sequentialWords) {
-            final SpellCheckerService service = mInternalServiceRef.get();
-            if (service == null) return;
             try {
                 mListener.onGetSuggestions(
-                        service.getSuggestionsMultiple(textInfos, mLocale,
-                                suggestionsLimit, sequentialWords));
+                        mSession.onGetSuggestionsMultiple(
+                                textInfos, suggestionsLimit, sequentialWords));
             } catch (RemoteException e) {
             }
         }
 
         @Override
-        public void cancel() {
-            final SpellCheckerService service = mInternalServiceRef.get();
-            if (service == null) return;
-            service.cancel();
+        public void onCancel() {
+            mSession.onCancel();
+        }
+
+        public String getLocale() {
+            return mLocale;
+        }
+
+        public Bundle getBundle() {
+            return mBundle;
         }
     }
 
@@ -137,10 +188,14 @@
 
         @Override
         public ISpellCheckerSession getISpellCheckerSession(
-                String locale, ISpellCheckerSessionListener listener) {
+                String locale, ISpellCheckerSessionListener listener, Bundle bundle) {
             final SpellCheckerService service = mInternalServiceRef.get();
             if (service == null) return null;
-            return new SpellCheckerSessionImpl(service, locale, listener);
+            final Session session = service.createSession();
+            final InternalISpellCheckerSession internalSession =
+                    new InternalISpellCheckerSession(locale, listener, bundle, session);
+            session.onCreate();
+            return internalSession;
         }
     }
 }
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index bf07e71..b940b80 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -233,7 +233,7 @@
                 Log.w(TAG, "Cancel spell checker tasks.");
             }
             try {
-                mISpellCheckerSession.cancel();
+                mISpellCheckerSession.onCancel();
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to cancel " + e);
             }
@@ -247,7 +247,7 @@
                 Log.w(TAG, "Get suggestions from the spell checker.");
             }
             try {
-                mISpellCheckerSession.getSuggestionsMultiple(
+                mISpellCheckerSession.onGetSuggestionsMultiple(
                         scp.mTextInfos, scp.mSuggestionsLimit, scp.mSequentialWords);
             } catch (RemoteException e) {
                 Log.e(TAG, "Failed to get suggestions " + e);
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index a60eb24..d60ce4f 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -19,11 +19,11 @@
 import com.android.internal.textservice.ITextServicesManager;
 
 import android.content.Context;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
-import android.view.textservice.SpellCheckerSession;
 import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener;
 
 import java.util.Locale;
@@ -74,7 +74,7 @@
      */
     // TODO: Add a method to get enabled spell checkers.
     // TODO: Handle referToSpellCheckerLanguageSettings
-    public SpellCheckerSession newSpellCheckerSession(Locale locale,
+    public SpellCheckerSession newSpellCheckerSession(Bundle bundle, Locale locale,
             SpellCheckerSessionListener listener, boolean referToSpellCheckerLanguageSettings) {
         if (listener == null) {
             throw new NullPointerException();
@@ -94,7 +94,7 @@
         try {
             sService.getSpellCheckerService(sci.getId(), localeString,
                     session.getTextServicesSessionListener(),
-                    session.getSpellCheckerSessionListener());
+                    session.getSpellCheckerSessionListener(), bundle);
         } catch (RemoteException e) {
             return null;
         }
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
index ff00492..67d7b3e 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerService.aidl
@@ -19,11 +19,13 @@
 import com.android.internal.textservice.ISpellCheckerSession;
 import com.android.internal.textservice.ISpellCheckerSessionListener;
 
+import android.os.Bundle;
+
 /**
  * Public interface to the global spell checker.
  * @hide
  */
 interface ISpellCheckerService {
     ISpellCheckerSession getISpellCheckerSession(
-            String locale, ISpellCheckerSessionListener listener);
+            String locale, ISpellCheckerSessionListener listener, in Bundle bundle);
 }
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
index 79e43510c0..5a00603 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
@@ -22,7 +22,7 @@
  * @hide
  */
 oneway interface ISpellCheckerSession {
-    void getSuggestionsMultiple(
+    void onGetSuggestionsMultiple(
             in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords);
-    void cancel();
+    void onCancel();
 }
diff --git a/core/java/com/android/internal/textservice/ITextServicesManager.aidl b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
index 4d7dfbb..bb4b2a3 100644
--- a/core/java/com/android/internal/textservice/ITextServicesManager.aidl
+++ b/core/java/com/android/internal/textservice/ITextServicesManager.aidl
@@ -20,6 +20,7 @@
 import com.android.internal.textservice.ITextServicesSessionListener;
 
 import android.content.ComponentName;
+import android.os.Bundle;
 import android.view.textservice.SpellCheckerInfo;
 
 /**
@@ -30,7 +31,7 @@
     SpellCheckerInfo getCurrentSpellChecker(String locale);
     oneway void getSpellCheckerService(String sciId, in String locale,
             in ITextServicesSessionListener tsListener,
-            in ISpellCheckerSessionListener scListener);
+            in ISpellCheckerSessionListener scListener, in Bundle bundle);
     oneway void finishSpellCheckerService(in ISpellCheckerSessionListener listener);
     oneway void setCurrentSpellChecker(String sciId);
     SpellCheckerInfo[] getEnabledSpellCheckers();
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index e97df84..238b747 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -31,19 +31,17 @@
 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.RemoteException;
-import android.os.SystemClock;
 import android.provider.Settings;
-import android.text.TextUtils;
 import android.service.textservice.SpellCheckerService;
-import android.util.Log;
+import android.text.TextUtils;
 import android.util.Slog;
 import android.view.textservice.SpellCheckerInfo;
 
 import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 
 public class TextServicesManagerService extends ITextServicesManager.Stub {
@@ -180,7 +178,8 @@
 
     @Override
     public void getSpellCheckerService(String sciId, String locale,
-            ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener) {
+            ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
+            Bundle bundle) {
         if (!mSystemReady) {
             return;
         }
@@ -199,7 +198,7 @@
                 if (bindGroup != null) {
                     final InternalDeathRecipient recipient =
                             mSpellCheckerBindGroups.get(sciId).addListener(
-                                    tsListener, locale, scListener, uid);
+                                    tsListener, locale, scListener, uid, bundle);
                     if (recipient == null) {
                         if (DBG) {
                             Slog.w(TAG, "Didn't create a death recipient.");
@@ -217,7 +216,7 @@
                         try {
                             final ISpellCheckerSession session =
                                     bindGroup.mSpellChecker.getISpellCheckerSession(
-                                            recipient.mScLocale, recipient.mScListener);
+                                            recipient.mScLocale, recipient.mScListener, bundle);
                             if (session != null) {
                                 tsListener.onServiceConnected(session);
                                 return;
@@ -236,7 +235,8 @@
             }
             final long ident = Binder.clearCallingIdentity();
             try {
-                startSpellCheckerServiceInnerLocked(sci, locale, tsListener, scListener, uid);
+                startSpellCheckerServiceInnerLocked(
+                        sci, locale, tsListener, scListener, uid, bundle);
             } finally {
                 Binder.restoreCallingIdentity(ident);
             }
@@ -246,13 +246,13 @@
 
     private void startSpellCheckerServiceInnerLocked(SpellCheckerInfo info, String locale,
             ITextServicesSessionListener tsListener, ISpellCheckerSessionListener scListener,
-            int uid) {
+            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, scListener);
+                sciId, locale, scListener, bundle);
         final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE);
         serviceIntent.setComponent(info.getComponent());
         if (DBG) {
@@ -263,7 +263,7 @@
             return;
         }
         final SpellCheckerBindGroup group = new SpellCheckerBindGroup(
-                connection, tsListener, locale, scListener, uid);
+                connection, tsListener, locale, scListener, uid, bundle);
         mSpellCheckerBindGroups.put(sciId, group);
     }
 
@@ -332,10 +332,10 @@
 
         public SpellCheckerBindGroup(InternalServiceConnection connection,
                 ITextServicesSessionListener listener, String locale,
-                ISpellCheckerSessionListener scListener, int uid) {
+                ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
             mInternalConnection = connection;
             mConnected = false;
-            addListener(listener, locale, scListener, uid);
+            addListener(listener, locale, scListener, uid, bundle);
         }
 
         public void onServiceConnected(ISpellCheckerService spellChecker) {
@@ -346,7 +346,7 @@
                 for (InternalDeathRecipient listener : mListeners) {
                     try {
                         final ISpellCheckerSession session = spellChecker.getISpellCheckerSession(
-                                listener.mScLocale, listener.mScListener);
+                                listener.mScLocale, listener.mScListener, listener.mBundle);
                         listener.mTsListener.onServiceConnected(session);
                     } catch (RemoteException e) {
                         Slog.e(TAG, "Exception in getting the spell checker session: " + e);
@@ -360,7 +360,7 @@
         }
 
         public InternalDeathRecipient addListener(ITextServicesSessionListener tsListener,
-                String locale, ISpellCheckerSessionListener scListener, int uid) {
+                String locale, ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
             if (DBG) {
                 Slog.d(TAG, "addListener: " + locale);
             }
@@ -375,7 +375,7 @@
                         }
                     }
                     recipient = new InternalDeathRecipient(
-                            this, tsListener, locale, scListener, uid);
+                            this, tsListener, locale, scListener, uid, bundle);
                     scListener.asBinder().linkToDeath(recipient, 0);
                     mListeners.add(recipient);
                 } catch(RemoteException e) {
@@ -440,11 +440,13 @@
         private final ISpellCheckerSessionListener mListener;
         private final String mSciId;
         private final String mLocale;
+        private final Bundle mBundle;
         public InternalServiceConnection(
-                String id, String locale, ISpellCheckerSessionListener listener) {
+                String id, String locale, ISpellCheckerSessionListener listener, Bundle bundle) {
             mSciId = id;
             mLocale = locale;
             mListener = listener;
+            mBundle = bundle;
         }
 
         @Override
@@ -473,14 +475,16 @@
         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) {
+                ISpellCheckerSessionListener scListener, int uid, Bundle bundle) {
             mTsListener = tsListener;
             mScListener = scListener;
             mScLocale = scLocale;
             mGroup = group;
             mUid = uid;
+            mBundle = bundle;
         }
 
         public boolean hasSpellCheckerListener(ISpellCheckerSessionListener listener) {