Merge "More robust check around TelephonyManager init."
diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java
index f8d0142..75dcd6e 100644
--- a/core/java/android/speech/tts/AudioPlaybackHandler.java
+++ b/core/java/android/speech/tts/AudioPlaybackHandler.java
@@ -415,6 +415,9 @@
         }
 
         if (params.mBytesWritten < params.mAudioBufferSize) {
+            if (DBG) Log.d(TAG, "Stopping audio track to flush audio, state was : " +
+                    audioTrack.getPlayState());
+            params.mIsShortUtterance = true;
             audioTrack.stop();
         }
 
@@ -452,10 +455,40 @@
             return;
         }
 
+        if (params.mIsShortUtterance) {
+            // In this case we would have called AudioTrack#stop() to flush
+            // buffers to the mixer. This makes the playback head position
+            // unobservable and notification markers do not work reliably. We
+            // have no option but to wait until we think the track would finish
+            // playing and release it after.
+            //
+            // This isn't as bad as it looks because (a) We won't end up waiting
+            // for much longer than we should because even at 4khz mono, a short
+            // utterance weighs in at about 2 seconds, and (b) such short utterances
+            // are expected to be relatively infrequent and in a stream of utterances
+            // this shows up as a slightly longer pause.
+            blockUntilEstimatedCompletion(params);
+        } else {
+            blockUntilCompletion(params);
+        }
+    }
+
+    private static void blockUntilEstimatedCompletion(SynthesisMessageParams params) {
+        final int lengthInFrames = params.mBytesWritten / params.mBytesPerFrame;
+        final long estimatedTimeMs = (lengthInFrames * 1000 / params.mSampleRateInHz);
+
+        if (DBG) Log.d(TAG, "About to sleep for: " + estimatedTimeMs + "ms for a short utterance");
+
+        try {
+            Thread.sleep(estimatedTimeMs);
+        } catch (InterruptedException ie) {
+            // Do nothing.
+        }
+    }
+
+    private static void blockUntilCompletion(SynthesisMessageParams params) {
         final AudioTrack audioTrack = params.mAudioTrack;
-        final int bytesPerFrame = params.mBytesPerFrame;
-        final int lengthInBytes = params.mBytesWritten;
-        final int lengthInFrames = lengthInBytes / bytesPerFrame;
+        final int lengthInFrames = params.mBytesWritten / params.mBytesPerFrame;
 
         int currentPosition = 0;
         while ((currentPosition = audioTrack.getPlaybackHeadPosition()) < lengthInFrames) {
diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java
index 3e905d6..779721e 100644
--- a/core/java/android/speech/tts/SynthesisMessageParams.java
+++ b/core/java/android/speech/tts/SynthesisMessageParams.java
@@ -41,7 +41,13 @@
     // Written by the synthesis thread, but read on the audio playback
     // thread.
     volatile int mBytesWritten;
+    // A "short utterance" is one that uses less bytes than the audio
+    // track buffer size (mAudioBufferSize). In this case, we need to call
+    // AudioTrack#stop() to send pending buffers to the mixer, and slightly
+    // different logic is required to wait for the track to finish.
+    //
     // Not volatile, accessed only from the audio playback thread.
+    boolean mIsShortUtterance;
     int mAudioBufferSize;
     // Always synchronized on "this".
     int mUnconsumedBytes;
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index da5baf8..302be57 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1583,6 +1583,14 @@
     /**
      * Set additional input method subtypes. Only a process which shares the same uid with the IME
      * can add additional input method subtypes to the IME.
+     * Please note that a subtype's status is stored in the system.
+     * For example, enabled subtypes are remembered by the framework even after they are removed
+     * by using this method. If you re-add the same subtypes again,
+     * they will just get enabled. If you want to avoid such conflicts, for instance, you may
+     * want to create a "different" new subtype even with the same locale and mode,
+     * by changing its extra value. The different subtype won't get affected by the stored past
+     * status. (You may want to take a look at {@link InputMethodSubtype#hashCode()} to refer
+     * to the current implementation.)
      * @param imiId Id of InputMethodInfo which additional input method subtypes will be added to.
      * @param subtypes subtypes will be added as additional subtypes of the current input method.
      * @return true if the additional input method subtypes are successfully added.
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index c85b2d9..01587aa 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -66,7 +66,13 @@
 
     /**
      * Get a spell checker session for the specified spell checker
-     * @param locale the locale for the spell checker
+     * @param locale the locale for the spell checker. If {@param locale} is null and
+     * referToSpellCheckerLanguageSettings is true, the locale specified in Settings will be
+     * returned. If {@param locale} is not null and referToSpellCheckerLanguageSettings is true,
+     * the locale specified in Settings will be returned only when it is same as {@param locale}.
+     * Exceptionally, when referToSpellCheckerLanguageSettings is true and {@param locale} is
+     * only language (e.g. "en"), the specified locale in Settings (e.g. "en_US") will be
+     * selected.
      * @param listener a spell checker session lister for getting results from a spell checker.
      * @param referToSpellCheckerLanguageSettings if true, the session for one of enabled
      * languages in settings will be returned.
@@ -81,6 +87,11 @@
             throw new IllegalArgumentException("Locale should not be null if you don't refer"
                     + " settings.");
         }
+
+        if (referToSpellCheckerLanguageSettings && !isSpellCheckerEnabled()) {
+            return null;
+        }
+
         final SpellCheckerInfo sci;
         try {
             sci = sService.getCurrentSpellChecker(null);
@@ -108,7 +119,12 @@
             final String localeStr = locale.toString();
             for (int i = 0; i < sci.getSubtypeCount(); ++i) {
                 final SpellCheckerSubtype subtype = sci.getSubtypeAt(i);
-                if (subtype.getLocale().equals(localeStr)) {
+                final String tempSubtypeLocale = subtype.getLocale();
+                if (tempSubtypeLocale.equals(localeStr)) {
+                    subtypeInUse = subtype;
+                    break;
+                } else if (localeStr.length() >= 2 && tempSubtypeLocale.length() >= 2
+                        && localeStr.startsWith(tempSubtypeLocale)) {
                     subtypeInUse = subtype;
                 }
             }
diff --git a/data/fonts/fonts.mk b/data/fonts/fonts.mk
index d8c1fa2..cacbdaa 100644
--- a/data/fonts/fonts.mk
+++ b/data/fonts/fonts.mk
@@ -15,10 +15,10 @@
 # Warning: this is actually a product definition, to be inherited from
 
 PRODUCT_COPY_FILES := \
-	frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \
-	frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
-	frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
-	frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
+    frameworks/base/data/fonts/Roboto-Regular.ttf:system/fonts/Roboto-Regular.ttf \
+    frameworks/base/data/fonts/Roboto-Bold.ttf:system/fonts/Roboto-Bold.ttf \
+    frameworks/base/data/fonts/Roboto-Italic.ttf:system/fonts/Roboto-Italic.ttf \
+    frameworks/base/data/fonts/Roboto-BoldItalic.ttf:system/fonts/Roboto-BoldItalic.ttf \
     frameworks/base/data/fonts/DroidSans.ttf:system/fonts/DroidSans.ttf \
     frameworks/base/data/fonts/DroidSans-Bold.ttf:system/fonts/DroidSans-Bold.ttf \
     frameworks/base/data/fonts/DroidNaskh-Regular.ttf:system/fonts/DroidNaskh-Regular.ttf \
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index b4d94f3..896f81e 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -1349,7 +1349,7 @@
         if (text == null) {
             throw new IllegalArgumentException("text cannot be null");
         }
-        if ((index | count) < 0 || index + count > text.length) {
+        if (index < 0 || text.length - index < Math.abs(count)) {
             throw new ArrayIndexOutOfBoundsException();
         }
 
diff --git a/libs/gui/tests/Android.mk b/libs/gui/tests/Android.mk
index 0308af3..55ac133 100644
--- a/libs/gui/tests/Android.mk
+++ b/libs/gui/tests/Android.mk
@@ -1,4 +1,4 @@
-# Build the unit tests.
+# Build the unit tests,
 LOCAL_PATH:= $(call my-dir)
 include $(CLEAR_VARS)
 
@@ -22,17 +22,15 @@
 	libui \
 	libutils \
 
-LOCAL_STATIC_LIBRARIES := \
-	libgtest \
-	libgtest_main \
-
 LOCAL_C_INCLUDES := \
     bionic \
     bionic/libstdc++/include \
     external/gtest/include \
     external/stlport/stlport \
 
-include $(BUILD_EXECUTABLE)
+# Build the binary to $(TARGET_OUT_DATA_NATIVE_TESTS)/$(LOCAL_MODULE)
+# to integrate with auto-test framework.
+include $(BUILD_NATIVE_TEST)
 
 # Include subdirectory makefiles
 # ============================================================
diff --git a/media/java/android/media/RemoteControlClient.java b/media/java/android/media/RemoteControlClient.java
index d59bc2b..d7b85e4 100644
--- a/media/java/android/media/RemoteControlClient.java
+++ b/media/java/android/media/RemoteControlClient.java
@@ -22,6 +22,7 @@
 import android.graphics.Canvas;
 import android.graphics.Paint;
 import android.graphics.RectF;
+import android.media.MediaMetadataRetriever;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Looper;
@@ -29,6 +30,7 @@
 import android.os.RemoteException;
 import android.util.Log;
 
+import java.lang.IllegalArgumentException;
 import java.util.HashMap;
 
 /**
@@ -236,6 +238,23 @@
         mEventHandler = new EventHandler(this, looper);
     }
 
+    private static final int[] METADATA_KEYS_TYPE_STRING = {
+        MediaMetadataRetriever.METADATA_KEY_ALBUM,
+        MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
+        MediaMetadataRetriever.METADATA_KEY_TITLE,
+        MediaMetadataRetriever.METADATA_KEY_ARTIST,
+        MediaMetadataRetriever.METADATA_KEY_AUTHOR,
+        MediaMetadataRetriever.METADATA_KEY_COMPILATION,
+        MediaMetadataRetriever.METADATA_KEY_COMPOSER,
+        MediaMetadataRetriever.METADATA_KEY_DATE,
+        MediaMetadataRetriever.METADATA_KEY_GENRE,
+        MediaMetadataRetriever.METADATA_KEY_TITLE,
+        MediaMetadataRetriever.METADATA_KEY_WRITER };
+    private static final int[] METADATA_KEYS_TYPE_LONG = {
+        MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
+        MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
+        MediaMetadataRetriever.METADATA_KEY_DURATION };
+
     /**
      * Class used to modify metadata in a {@link RemoteControlClient} object.
      */
@@ -256,6 +275,11 @@
         }
 
         /**
+         * The metadata key for the content artwork / album art.
+         */
+        public final static int METADATA_KEY_ARTWORK = 100;
+
+        /**
          * Adds textual information to be displayed.
          * Note that none of the information added after {@link #apply()} has been called,
          * will be displayed.
@@ -265,49 +289,73 @@
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR},
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE},
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE},
          *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER},
-         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
-         * @param value the text for the given key, or null to signify there is no valid
+         *      .
+         * @param value the text for the given key, or {@code null} to signify there is no valid
          *      information for the field.
          * @return      FIXME description
          */
-        public synchronized MetadataEditor putString(int key, String value) {
+        public synchronized MetadataEditor putString(int key, String value)
+                throws IllegalArgumentException {
             if (mApplied) {
                 Log.e(TAG, "Can't edit a previously applied MetadataEditor");
                 return this;
             }
+            if (!validTypeForKey(key, METADATA_KEYS_TYPE_STRING)) {
+                throw(new IllegalArgumentException("Invalid type 'String' for key "+ key));
+            }
             mEditorMetadata.putString(String.valueOf(key), value);
             mMetadataChanged = true;
             return this;
         }
 
         /**
-         * The metadata key for the content artwork / album art.
+         * FIXME javadoc
+         * @param key the identifier of a the metadata field to set. Valid values are
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER},
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value
+         *      expressed in milliseconds),
+         *      {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}.
+         * @param value FIXME javadoc
+         * @return FIXME javadoc
+         * @throws IllegalArgumentException
          */
-        public final int METADATA_KEY_ARTWORK = 100;
+        public synchronized MetadataEditor putLong(int key, long value)
+                throws IllegalArgumentException {
+            if (mApplied) {
+                Log.e(TAG, "Can't edit a previously applied MetadataEditor");
+                return this;
+            }
+            if (!validTypeForKey(key, METADATA_KEYS_TYPE_LONG)) {
+                throw(new IllegalArgumentException("Invalid type 'long' for key "+ key));
+            }
+            mEditorMetadata.putLong(String.valueOf(key), value);
+            mMetadataChanged = true;
+            return this;
+        }
 
         /**
          * Sets the album / artwork picture to be displayed on the remote control.
          * @param key FIXME description
          * @param bitmap the bitmap for the artwork, or null if there isn't any.
          * @return FIXME description
+         * @throws IllegalArgumentException
          * @see android.graphics.Bitmap
          */
-        public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap) {
+        public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap)
+                throws IllegalArgumentException {
             if (mApplied) {
                 Log.e(TAG, "Can't edit a previously applied MetadataEditor");
                 return this;
             }
             if (key != METADATA_KEY_ARTWORK) {
-                return this;
+                throw(new IllegalArgumentException("Invalid type 'Bitmap' for key "+ key));
             }
             if ((mArtworkExpectedWidth > 0) && (mArtworkExpectedHeight > 0)) {
                 mEditorArtwork = scaleBitmapIfTooBig(bitmap,
@@ -740,6 +788,24 @@
             }
         }
         return bitmap;
+    }
 
+    /**
+     *  Fast routine to go through an array of allowed keys and return whether the key is part
+     *  of that array
+     * @param key the key value
+     * @param validKeys the array of valid keys for a given type
+     * @return true if the key is part of the array, false otherwise
+     */
+    private static boolean validTypeForKey(int key, int[] validKeys) {
+        try {
+            for (int i = 0 ; ; i++) {
+                if (key == validKeys[i]) {
+                    return true;
+                }
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return false;
+        }
     }
 }
diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
index a6a3a18..f41e9d2 100644
--- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp
@@ -42,7 +42,7 @@
 
 void NuPlayer::StreamingSource::start() {
     mStreamListener = new NuPlayerStreamListener(mSource, 0);
-    mTSParser = new ATSParser;
+    mTSParser = new ATSParser(ATSParser::TS_TIMESTAMPS_ARE_ABSOLUTE);
 
     mStreamListener->start();
 }
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 142dda0..f98b0de 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -513,7 +513,8 @@
     // If we did this later, audio would continue playing while we
     // shutdown the video-related resources and the player appear to
     // not be as responsive to a reset request.
-    if (mAudioPlayer == NULL && mAudioSource != NULL) {
+    if ((mAudioPlayer == NULL || !(mFlags & AUDIOPLAYER_STARTED))
+            && mAudioSource != NULL) {
         // If we had an audio player, it would have effectively
         // taken possession of the audio source and stopped it when
         // _it_ is stopped. Otherwise this is still our responsibility.
diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp
index 5bbc2b4..74a3b32 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.cpp
+++ b/media/libstagefright/mpeg2ts/ATSParser.cpp
@@ -325,14 +325,16 @@
 }
 
 int64_t ATSParser::Program::convertPTSToTimestamp(uint64_t PTS) {
-    if (!mFirstPTSValid) {
-        mFirstPTSValid = true;
-        mFirstPTS = PTS;
-        PTS = 0;
-    } else if (PTS < mFirstPTS) {
-        PTS = 0;
-    } else {
-        PTS -= mFirstPTS;
+    if (!(mParser->mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)) {
+        if (!mFirstPTSValid) {
+            mFirstPTSValid = true;
+            mFirstPTS = PTS;
+            PTS = 0;
+        } else if (PTS < mFirstPTS) {
+            PTS = 0;
+        } else {
+            PTS -= mFirstPTS;
+        }
     }
 
     return (PTS * 100) / 9;
@@ -734,7 +736,8 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
-ATSParser::ATSParser() {
+ATSParser::ATSParser(uint32_t flags)
+    : mFlags(flags) {
 }
 
 ATSParser::~ATSParser() {
diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h
index 1e6451d..d12d998 100644
--- a/media/libstagefright/mpeg2ts/ATSParser.h
+++ b/media/libstagefright/mpeg2ts/ATSParser.h
@@ -38,7 +38,16 @@
         DISCONTINUITY_FORMATCHANGE
     };
 
-    ATSParser();
+    enum Flags {
+        // The 90kHz clock (PTS/DTS) is absolute, i.e. PTS=0 corresponds to
+        // a media time of 0.
+        // If this flag is _not_ specified, the first PTS encountered in a
+        // program of this stream will be assumed to correspond to media time 0
+        // instead.
+        TS_TIMESTAMPS_ARE_ABSOLUTE = 1
+    };
+
+    ATSParser(uint32_t flags = 0);
 
     void feedTSPacket(const void *data, size_t size);
 
@@ -73,6 +82,7 @@
     struct Program;
     struct Stream;
 
+    uint32_t mFlags;
     Vector<sp<Program> > mPrograms;
 
     void parseProgramAssociationTable(ABitReader *br);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
index 1e417ac..5911378 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/InputMethodsPanel.java
@@ -428,7 +428,7 @@
 
     private CharSequence getIMIName(InputMethodInfo imi) {
         if (imi == null) return null;
-        return mPackageManager.getApplicationLabel(imi.getServiceInfo().applicationInfo);
+        return imi.loadLabel(mPackageManager);
     }
 
     private CharSequence getSubtypeName(InputMethodInfo imi, InputMethodSubtype subtype) {
diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java
index 38bcebc..bb831f5 100644
--- a/services/java/com/android/server/InputMethodManagerService.java
+++ b/services/java/com/android/server/InputMethodManagerService.java
@@ -1682,7 +1682,12 @@
                 for (int i = 0; i < packageNum; ++i) {
                     if (packageInfos[i].equals(imi.getPackageName())) {
                         mFileManager.addInputMethodSubtypes(imi, subtypes);
-                        buildInputMethodListLocked(mMethodList, mMethodMap);
+                        final long ident = Binder.clearCallingIdentity();
+                        try {
+                            buildInputMethodListLocked(mMethodList, mMethodMap);
+                        } finally {
+                            Binder.restoreCallingIdentity(ident);
+                        }
                         return true;
                     }
                 }
@@ -1707,7 +1712,7 @@
                 return;
             }
 
-            long ident = Binder.clearCallingIdentity();
+            final long ident = Binder.clearCallingIdentity();
             try {
                 setInputMethodLocked(id, subtypeId);
             } finally {
@@ -3065,17 +3070,11 @@
         public void addInputMethodSubtypes(
                 InputMethodInfo imi, InputMethodSubtype[] additionalSubtypes) {
             synchronized (mMethodMap) {
-                final HashSet<InputMethodSubtype> existingSubtypes =
-                        new HashSet<InputMethodSubtype>();
-                for (int i = 0; i < imi.getSubtypeCount(); ++i) {
-                    existingSubtypes.add(imi.getSubtypeAt(i));
-                }
-
                 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
                 final int N = additionalSubtypes.length;
                 for (int i = 0; i < N; ++i) {
                     final InputMethodSubtype subtype = additionalSubtypes[i];
-                    if (!subtypes.contains(subtype) && !existingSubtypes.contains(subtype)) {
+                    if (!subtypes.contains(subtype)) {
                         subtypes.add(subtype);
                     }
                 }
diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java
index f6c369e..c792b33 100644
--- a/services/java/com/android/server/TextServicesManagerService.java
+++ b/services/java/com/android/server/TextServicesManagerService.java
@@ -201,7 +201,7 @@
                     Settings.Secure.getString(mContext.getContentResolver(),
                             Settings.Secure.SELECTED_SPELL_CHECKER_SUBTYPE);
             if (DBG) {
-                Slog.w(TAG, "getCurrentSpellChecker: " + subtypeHashCodeStr);
+                Slog.w(TAG, "getCurrentSpellCheckerSubtype: " + subtypeHashCodeStr);
             }
             final SpellCheckerInfo sci = getCurrentSpellChecker(null);
             if (sci == null || sci.getSubtypeCount() == 0) {
@@ -509,7 +509,8 @@
                                 listener.mScLocale, listener.mScListener, listener.mBundle);
                         listener.mTsListener.onServiceConnected(session);
                     } catch (RemoteException e) {
-                        Slog.e(TAG, "Exception in getting the spell checker session: " + e);
+                        Slog.e(TAG, "Exception in getting the spell checker session."
+                                + "Reconnect to the spellchecker. ", e);
                         removeAll();
                         return;
                     }
@@ -579,8 +580,12 @@
                 Slog.d(TAG, "cleanLocked");
             }
             if (mListeners.isEmpty()) {
-                if (mSpellCheckerBindGroups.containsKey(this)) {
-                    mSpellCheckerBindGroups.remove(this);
+                final String sciId = mInternalConnection.mSciId;
+                if (mSpellCheckerBindGroups.containsKey(sciId)) {
+                    if (DBG) {
+                        Slog.d(TAG, "Remove bind group.");
+                    }
+                    mSpellCheckerBindGroups.remove(sciId);
                 }
                 // Unbind service when there is no active clients.
                 mContext.unbindService(mInternalConnection);
diff --git a/tests/BandwidthTests/Android.mk b/tests/BandwidthTests/Android.mk
index 2cc2009..7bc5f857 100644
--- a/tests/BandwidthTests/Android.mk
+++ b/tests/BandwidthTests/Android.mk
@@ -16,7 +16,9 @@
 LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 
+LOCAL_MODULE_TAGS := tests
+
 LOCAL_PACKAGE_NAME := BandwidthEnforcementTest
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
 
-include $(BUILD_PACKAGE)
\ No newline at end of file
+include $(BUILD_PACKAGE)