Add Java API for writing TTS engines

This removes the old non-public C++ API for TTS
engines and replaces it with a Java API.

The new API is still @hidden, until it has been approved.

Bug: 4148636
Change-Id: I7614ff788e11f897e87052f684f1b4938d539fb7
diff --git a/Android.mk b/Android.mk
index 28340cf..cd9ae7d 100644
--- a/Android.mk
+++ b/Android.mk
@@ -137,8 +137,8 @@
 	core/java/android/view/IWindowSession.aidl \
 	core/java/android/speech/IRecognitionListener.aidl \
 	core/java/android/speech/IRecognitionService.aidl \
-	core/java/android/speech/tts/ITts.aidl \
-	core/java/android/speech/tts/ITtsCallback.aidl \
+	core/java/android/speech/tts/ITextToSpeechCallback.aidl \
+	core/java/android/speech/tts/ITextToSpeechService.aidl \
 	core/java/com/android/internal/app/IBatteryStats.aidl \
 	core/java/com/android/internal/app/IUsageStats.aidl \
 	core/java/com/android/internal/app/IMediaContainerService.aidl \
diff --git a/CleanSpec.mk b/CleanSpec.mk
index 023ce59..50292e4 100644
--- a/CleanSpec.mk
+++ b/CleanSpec.mk
@@ -96,6 +96,7 @@
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/RSTest_intermediates/)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/hardware/IUsbManager.java)
 $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/core/java/android/nfc)
+$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates)
 
 # ************************************************
 # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
diff --git a/core/java/android/speech/tts/BlockingMediaPlayer.java b/core/java/android/speech/tts/BlockingMediaPlayer.java
new file mode 100644
index 0000000..3cf60dd
--- /dev/null
+++ b/core/java/android/speech/tts/BlockingMediaPlayer.java
@@ -0,0 +1,146 @@
+/*
+ * 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 android.speech.tts;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Looper;
+import android.util.Log;
+
+/**
+ * A media player that allows blocking to wait for it to finish.
+ */
+class BlockingMediaPlayer {
+
+    private static final String TAG = "BlockMediaPlayer";
+
+    private static final String MEDIA_PLAYER_THREAD_NAME = "TTS-MediaPlayer";
+
+    private final Context mContext;
+    private final Uri mUri;
+    private final int mStreamType;
+    private final ConditionVariable mDone;
+    // Only accessed on the Handler thread
+    private MediaPlayer mPlayer;
+    private volatile boolean mFinished;
+
+    /**
+     * Creates a new blocking media player.
+     * Creating a blocking media player is a cheap operation.
+     *
+     * @param context
+     * @param uri
+     * @param streamType
+     */
+    public BlockingMediaPlayer(Context context, Uri uri, int streamType) {
+        mContext = context;
+        mUri = uri;
+        mStreamType = streamType;
+        mDone = new ConditionVariable();
+
+    }
+
+    /**
+     * Starts playback and waits for it to finish.
+     * Can be called from any thread.
+     *
+     * @return {@code true} if the playback finished normally, {@code false} if the playback
+     *         failed or {@link #stop} was called before the playback finished.
+     */
+    public boolean startAndWait() {
+        HandlerThread thread = new HandlerThread(MEDIA_PLAYER_THREAD_NAME);
+        thread.start();
+        Handler handler = new Handler(thread.getLooper());
+        mFinished = false;
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                startPlaying();
+            }
+        });
+        mDone.block();
+        handler.post(new Runnable() {
+            @Override
+            public void run() {
+                finish();
+                // No new messages should get posted to the handler thread after this
+                Looper.myLooper().quit();
+            }
+        });
+        return mFinished;
+    }
+
+    /**
+     * Stops playback. Can be called multiple times.
+     * Can be called from any thread.
+     */
+    public void stop() {
+        mDone.open();
+    }
+
+    /**
+     * Starts playback.
+     * Called on the handler thread.
+     */
+    private void startPlaying() {
+        mPlayer = MediaPlayer.create(mContext, mUri);
+        if (mPlayer == null) {
+            Log.w(TAG, "Failed to play " + mUri);
+            mDone.open();
+            return;
+        }
+        try {
+            mPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
+                @Override
+                public boolean onError(MediaPlayer mp, int what, int extra) {
+                    Log.w(TAG, "Audio playback error: " + what + ", " + extra);
+                    mDone.open();
+                    return true;
+                }
+            });
+            mPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+                @Override
+                public void onCompletion(MediaPlayer mp) {
+                    mFinished = true;
+                    mDone.open();
+                }
+            });
+            mPlayer.setAudioStreamType(mStreamType);
+            mPlayer.start();
+        } catch (IllegalArgumentException ex) {
+            Log.w(TAG, "MediaPlayer failed", ex);
+            mDone.open();
+        }
+    }
+
+    /**
+     * Stops playback and release the media player.
+     * Called on the handler thread.
+     */
+    private void finish() {
+        try {
+            mPlayer.stop();
+        } catch (IllegalStateException ex) {
+            // Do nothing, the player is already stopped
+        }
+        mPlayer.release();
+    }
+
+}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/FileSynthesisRequest.java b/core/java/android/speech/tts/FileSynthesisRequest.java
new file mode 100644
index 0000000..370ad53
--- /dev/null
+++ b/core/java/android/speech/tts/FileSynthesisRequest.java
@@ -0,0 +1,197 @@
+/*
+ * 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 android.speech.tts;
+
+import android.media.AudioFormat;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+/**
+ * Speech synthesis request that writes the audio to a WAV file.
+ */
+class FileSynthesisRequest extends SynthesisRequest {
+
+    private static final String TAG = "FileSynthesisRequest";
+    private static final boolean DBG = false;
+
+    private static final int WAV_HEADER_LENGTH = 44;
+    private static final short WAV_FORMAT_PCM = 0x0001;
+
+    private final Object mStateLock = new Object();
+    private final File mFileName;
+    private int mSampleRateInHz;
+    private int mAudioFormat;
+    private int mChannelCount;
+    private RandomAccessFile mFile;
+    private boolean mStopped = false;
+
+    FileSynthesisRequest(String text, File fileName) {
+        super(text);
+        mFileName = fileName;
+    }
+
+    @Override
+    void stop() {
+        synchronized (mStateLock) {
+            mStopped = true;
+            cleanUp();
+        }
+    }
+
+    /**
+     * Must be called while holding the monitor on {@link #mStateLock}.
+     */
+    private void cleanUp() {
+        closeFile();
+        if (mFile != null) {
+            mFileName.delete();
+        }
+    }
+
+    /**
+     * Must be called while holding the monitor on {@link #mStateLock}.
+     */
+    private void closeFile() {
+        try {
+            if (mFile != null) {
+                mFile.close();
+                mFile = null;
+            }
+        } catch (IOException ex) {
+            Log.e(TAG, "Failed to close " + mFileName + ": " + ex);
+        }
+    }
+
+    @Override
+    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+        if (DBG) {
+            Log.d(TAG, "FileSynthesisRequest.start(" + sampleRateInHz + "," + audioFormat
+                    + "," + channelCount + ")");
+        }
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mFile != null) {
+                cleanUp();
+                throw new IllegalArgumentException("FileSynthesisRequest.start() called twice");
+            }
+            mSampleRateInHz = sampleRateInHz;
+            mAudioFormat = audioFormat;
+            mChannelCount = channelCount;
+            try {
+                mFile = new RandomAccessFile(mFileName, "rw");
+                // Reserve space for WAV header
+                mFile.write(new byte[WAV_HEADER_LENGTH]);
+                return TextToSpeech.SUCCESS;
+            } catch (IOException ex) {
+                Log.e(TAG, "Failed to open " + mFileName + ": " + ex);
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+        }
+    }
+
+    @Override
+    public int audioAvailable(byte[] buffer, int offset, int length) {
+        if (DBG) {
+            Log.d(TAG, "FileSynthesisRequest.audioAvailable(" + buffer + "," + offset
+                    + "," + length + ")");
+        }
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mFile == null) {
+                Log.e(TAG, "File not open");
+                return TextToSpeech.ERROR;
+            }
+            try {
+                mFile.write(buffer, offset, length);
+                return TextToSpeech.SUCCESS;
+            } catch (IOException ex) {
+                Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+        }
+    }
+
+    @Override
+    public int done() {
+        if (DBG) Log.d(TAG, "FileSynthesisRequest.done()");
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mFile == null) {
+                Log.e(TAG, "File not open");
+                return TextToSpeech.ERROR;
+            }
+            try {
+                // Write WAV header at start of file
+                mFile.seek(0);
+                int fileLen = (int) mFile.length();
+                mFile.write(makeWavHeader(mSampleRateInHz, mAudioFormat, mChannelCount, fileLen));
+                closeFile();
+                return TextToSpeech.SUCCESS;
+            } catch (IOException ex) {
+                Log.e(TAG, "Failed to write to " + mFileName + ": " + ex);
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+        }
+    }
+
+    private byte[] makeWavHeader(int sampleRateInHz, int audioFormat, int channelCount,
+            int fileLength) {
+        // TODO: is AudioFormat.ENCODING_DEFAULT always the same as ENCODING_PCM_16BIT?
+        int sampleSizeInBytes = (audioFormat == AudioFormat.ENCODING_PCM_8BIT ? 1 : 2);
+        int byteRate = sampleRateInHz * sampleSizeInBytes * channelCount;
+        short blockAlign = (short) (sampleSizeInBytes * channelCount);
+        short bitsPerSample = (short) (sampleSizeInBytes * 8);
+
+        byte[] headerBuf = new byte[WAV_HEADER_LENGTH];
+        ByteBuffer header = ByteBuffer.wrap(headerBuf);
+        header.order(ByteOrder.LITTLE_ENDIAN);
+
+        header.put(new byte[]{ 'R', 'I', 'F', 'F' });
+        header.putInt(fileLength - 8);  // RIFF chunk size
+        header.put(new byte[]{ 'W', 'A', 'V', 'E' });
+        header.put(new byte[]{ 'f', 'm', 't', ' ' });
+        header.putInt(16);  // size of fmt chunk
+        header.putShort(WAV_FORMAT_PCM);
+        header.putShort((short) channelCount);
+        header.putInt(sampleRateInHz);
+        header.putInt(byteRate);
+        header.putShort(blockAlign);
+        header.putShort(bitsPerSample);
+        header.put(new byte[]{ 'd', 'a', 't', 'a' });
+        int dataLength = fileLength - WAV_HEADER_LENGTH;
+        header.putInt(dataLength);
+
+        return headerBuf;
+    }
+
+}
diff --git a/core/java/android/speech/tts/ITtsCallback.aidl b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
similarity index 78%
rename from core/java/android/speech/tts/ITtsCallback.aidl
rename to core/java/android/speech/tts/ITextToSpeechCallback.aidl
index c9898eb..40902ae 100755
--- a/core/java/android/speech/tts/ITtsCallback.aidl
+++ b/core/java/android/speech/tts/ITextToSpeechCallback.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 The Android Open Source Project
+ * 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.
@@ -13,15 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package android.speech.tts;
 
 /**
- * AIDL for the callback from the TTS Service
- * ITtsCallback.java is autogenerated from this.
+ * Interface for callbacks from TextToSpeechService
  *
  * {@hide}
  */
-oneway interface ITtsCallback {
+oneway interface ITextToSpeechCallback {
     void utteranceCompleted(String utteranceId);
 }
diff --git a/core/java/android/speech/tts/ITextToSpeechService.aidl b/core/java/android/speech/tts/ITextToSpeechService.aidl
new file mode 100644
index 0000000..ff3fa11
--- /dev/null
+++ b/core/java/android/speech/tts/ITextToSpeechService.aidl
@@ -0,0 +1,140 @@
+/*
+ * 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 android.speech.tts;
+
+import android.net.Uri;
+import android.os.Bundle;
+import android.speech.tts.ITextToSpeechCallback;
+
+/**
+ * Interface for TextToSpeech to talk to TextToSpeechService.
+ *
+ * {@hide}
+ */
+interface ITextToSpeechService {
+
+    /**
+     * Tells the engine to synthesize some speech and play it back.
+     *
+     * @param callingApp The package name of the calling app. Used to connect requests
+     *         callbacks and to clear requests when the calling app is stopping.
+     * @param text The text to synthesize.
+     * @param queueMode Determines what to do to requests already in the queue.
+     * @param param Request parameters.
+     */
+    int speak(in String callingApp, in String text, in int queueMode, in Bundle params);
+
+    /**
+     * Tells the engine to synthesize some speech and write it to a file.
+     *
+     * @param callingApp The package name of the calling app. Used to connect requests
+     *         callbacks and to clear requests when the calling app is stopping.
+     * @param text The text to synthesize.
+     * @param filename The file to write the synthesized audio to.
+     * @param param Request parameters.
+     */
+    int synthesizeToFile(in String callingApp, in String text,
+        in String filename, in Bundle params);
+
+    /**
+     * Plays an existing audio resource.
+     *
+     * @param callingApp The package name of the calling app. Used to connect requests
+     *         callbacks and to clear requests when the calling app is stopping.
+     * @param audioUri URI for the audio resource (a file or android.resource URI)
+     * @param queueMode Determines what to do to requests already in the queue.
+     * @param param Request parameters.
+     */
+    int playAudio(in String callingApp, in Uri audioUri, in int queueMode, in Bundle params);
+
+    /**
+     * Plays silence.
+     *
+     * @param callingApp The package name of the calling app. Used to connect requests
+     *         callbacks and to clear requests when the calling app is stopping.
+     * @param duration Number of milliseconds of silence to play.
+     * @param queueMode Determines what to do to requests already in the queue.
+     * @param param Request parameters.
+     */
+    int playSilence(in String callingApp, in long duration, in int queueMode, in Bundle params);
+
+    /**
+     * Checks whether the service is currently playing some audio.
+     */
+    boolean isSpeaking();
+
+    /**
+     * Interrupts the current utterance (if from the given app) and removes any utterances
+     * in the queue that are from the given app.
+     *
+     * @param callingApp Package name of the app whose utterances
+     *        should be interrupted and cleared.
+     */
+    int stop(in String callingApp);
+
+    /**
+     * Returns the language, country and variant currently being used by the TTS engine.
+     *
+     * Can be called from multiple threads.
+     *
+     * @return A 3-element array, containing language (ISO 3-letter code),
+     *         country (ISO 3-letter code) and variant used by the engine.
+     *         The country and variant may be {@code ""}. If country is empty, then variant must
+     *         be empty too.
+     */
+    String[] getLanguage();
+
+    /**
+     * Checks whether the engine supports a given language.
+     *
+     * @param lang ISO-3 language code.
+     * @param country ISO-3 country code. May be empty or null.
+     * @param variant Language variant. May be empty or null.
+     * @return Code indicating the support status for the locale.
+     *         One of {@link TextToSpeech#LANG_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+     *         {@link TextToSpeech#LANG_MISSING_DATA}
+     *         {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+     */
+    int isLanguageAvailable(in String lang, in String country, in String variant);
+
+    /**
+     * Notifies the engine that it should load a speech synthesis language.
+     *
+     * @param lang ISO-3 language code.
+     * @param country ISO-3 country code. May be empty or null.
+     * @param variant Language variant. May be empty or null.
+     * @return Code indicating the support status for the locale.
+     *         One of {@link TextToSpeech#LANG_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+     *         {@link TextToSpeech#LANG_MISSING_DATA}
+     *         {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+     */
+    int loadLanguage(in String lang, in String country, in String variant);
+
+    /**
+     * Sets the callback that will be notified when playback of utterance from the
+     * given app are completed.
+     *
+     * @param callingApp Package name for the app whose utterance the callback will handle.
+     * @param cb The callback.
+     */
+    void setCallback(in String callingApp, ITextToSpeechCallback cb);
+
+}
diff --git a/core/java/android/speech/tts/ITts.aidl b/core/java/android/speech/tts/ITts.aidl
deleted file mode 100755
index c1051c4..0000000
--- a/core/java/android/speech/tts/ITts.aidl
+++ /dev/null
@@ -1,69 +0,0 @@
-/*

- * Copyright (C) 2009 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 android.speech.tts;

-

-import android.speech.tts.ITtsCallback;

-

-import android.content.Intent;

-

-/**

- * AIDL for the TTS Service

- * ITts.java is autogenerated from this.

- *

- * {@hide}

- */

-interface ITts {

-    int setSpeechRate(in String callingApp, in int speechRate);

-

-    int setPitch(in String callingApp, in int pitch);

-

-    int speak(in String callingApp, in String text, in int queueMode, in String[] params);

-

-    boolean isSpeaking();

-

-    int stop(in String callingApp);

-

-    void addSpeech(in String callingApp, in String text, in String packageName, in int resId);

-

-    void addSpeechFile(in String callingApp, in String text, in String filename);

-

-    String[] getLanguage();

-

-    int isLanguageAvailable(in String language, in String country, in String variant, in String[] params);

-

-    int setLanguage(in String callingApp, in String language, in String country, in String variant);

-

-    boolean synthesizeToFile(in String callingApp, in String text, in String[] params, in String outputDirectory);

-

-    int playEarcon(in String callingApp, in String earcon, in int queueMode, in String[] params);

-

-    void addEarcon(in String callingApp, in String earcon, in String packageName, in int resId);

-

-    void addEarconFile(in String callingApp, in String earcon, in String filename);

-

-    int registerCallback(in String callingApp, ITtsCallback cb);

-

-    int unregisterCallback(in String callingApp, ITtsCallback cb);

-

-    int playSilence(in String callingApp, in long duration, in int queueMode, in String[] params);
-

-    int setEngineByPackageName(in String enginePackageName);
-
-    String getDefaultEngine();
-
-    boolean areDefaultsEnforced();

-}

diff --git a/core/java/android/speech/tts/PlaybackSynthesisRequest.java b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
new file mode 100644
index 0000000..15a4ee9
--- /dev/null
+++ b/core/java/android/speech/tts/PlaybackSynthesisRequest.java
@@ -0,0 +1,197 @@
+/*
+ * 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 android.speech.tts;
+
+import android.media.AudioFormat;
+import android.media.AudioTrack;
+import android.util.Log;
+
+/**
+ * Speech synthesis request that plays the audio as it is received.
+ */
+class PlaybackSynthesisRequest extends SynthesisRequest {
+
+    private static final String TAG = "PlaybackSynthesisRequest";
+    private static final boolean DBG = false;
+
+    private static final int MIN_AUDIO_BUFFER_SIZE = 8192;
+
+    /**
+     * Audio stream type. Must be one of the STREAM_ contants defined in
+     * {@link android.media.AudioManager}.
+     */
+    private final int mStreamType;
+
+    /**
+     * Volume, in the range [0.0f, 1.0f]. The default value is
+     * {@link TextToSpeech.Engine#DEFAULT_VOLUME} (1.0f).
+     */
+    private final float mVolume;
+
+    /**
+     * Left/right position of the audio, in the range [-1.0f, 1.0f].
+     * The default value is {@link TextToSpeech.Engine#DEFAULT_PAN} (0.0f).
+     */
+    private final float mPan;
+
+    private final Object mStateLock = new Object();
+    private AudioTrack mAudioTrack = null;
+    private boolean mStopped = false;
+
+    PlaybackSynthesisRequest(String text, int streamType, float volume, float pan) {
+        super(text);
+        mStreamType = streamType;
+        mVolume = volume;
+        mPan = pan;
+    }
+
+    @Override
+    void stop() {
+        if (DBG) Log.d(TAG, "stop()");
+        synchronized (mStateLock) {
+            mStopped = true;
+            cleanUp();
+        }
+    }
+
+    private void cleanUp() {
+        if (DBG) Log.d(TAG, "cleanUp()");
+        if (mAudioTrack != null) {
+            mAudioTrack.flush();
+            mAudioTrack.stop();
+            // TODO: do we need to wait for playback to finish before releasing?
+            mAudioTrack.release();
+            mAudioTrack = null;
+        }
+    }
+
+    // TODO: add a thread that writes to the AudioTrack?
+    @Override
+    public int start(int sampleRateInHz, int audioFormat, int channelCount) {
+        if (DBG) {
+            Log.d(TAG, "start(" + sampleRateInHz + "," + audioFormat
+                    + "," + channelCount + ")");
+        }
+
+        int channelConfig;
+        if (channelCount == 1) {
+            channelConfig = AudioFormat.CHANNEL_OUT_MONO;
+        } else if (channelCount == 2){
+            channelConfig = AudioFormat.CHANNEL_OUT_STEREO;
+        } else {
+            Log.e(TAG, "Unsupported number of channels: " + channelCount);
+            return TextToSpeech.ERROR;
+        }
+
+        int minBufferSizeInBytes
+                = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);
+        int bufferSizeInBytes = Math.max(MIN_AUDIO_BUFFER_SIZE, minBufferSizeInBytes);
+
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mAudioTrack != null) {
+                Log.e(TAG, "start() called twice");
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+
+            mAudioTrack = new AudioTrack(mStreamType, sampleRateInHz, channelConfig, audioFormat,
+                    bufferSizeInBytes, AudioTrack.MODE_STREAM);
+            if (mAudioTrack.getState() != AudioTrack.STATE_INITIALIZED) {
+                cleanUp();
+                return TextToSpeech.ERROR;
+            }
+
+            setupVolume();
+        }
+
+        return TextToSpeech.SUCCESS;
+    }
+
+    private void setupVolume() {
+        float vol = clip(mVolume, 0.0f, 1.0f);
+        float panning = clip(mPan, -1.0f, 1.0f);
+        float volLeft = vol;
+        float volRight = vol;
+        if (panning > 0.0f) {
+            volLeft *= (1.0f - panning);
+        } else if (panning < 0.0f) {
+            volRight *= (1.0f + panning);
+        }
+        if (DBG) Log.d(TAG, "volLeft=" + volLeft + ",volRight=" + volRight);
+        if (mAudioTrack.setStereoVolume(volLeft, volRight) != AudioTrack.SUCCESS) {
+            Log.e(TAG, "Failed to set volume");
+        }
+    }
+
+    private float clip(float value, float min, float max) {
+        return value > max ? max : (value < min ? min : value);
+    }
+
+    @Override
+    public int audioAvailable(byte[] buffer, int offset, int length) {
+        if (DBG) {
+            Log.d(TAG, "audioAvailable(byte[" + buffer.length + "],"
+                    + offset + "," + length + "), thread ID=" + android.os.Process.myTid());
+        }
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mAudioTrack == null) {
+                Log.e(TAG, "audioAvailable(): Not started");
+                return TextToSpeech.ERROR;
+            }
+            int playState = mAudioTrack.getPlayState();
+            if (playState == AudioTrack.PLAYSTATE_STOPPED) {
+                if (DBG) Log.d(TAG, "AudioTrack stopped, restarting");
+                mAudioTrack.play();
+            }
+            // TODO: loop until all data is written?
+            if (DBG) Log.d(TAG, "AudioTrack.write()");
+            int count = mAudioTrack.write(buffer, offset, length);
+            if (DBG) Log.d(TAG, "AudioTrack.write() returned " + count);
+            if (count < 0) {
+                Log.e(TAG, "Writing to AudioTrack failed: " + count);
+                cleanUp();
+                return TextToSpeech.ERROR;
+            } else {
+                return TextToSpeech.SUCCESS;
+            }
+        }
+    }
+
+    @Override
+    public int done() {
+        if (DBG) Log.d(TAG, "done()");
+        synchronized (mStateLock) {
+            if (mStopped) {
+                if (DBG) Log.d(TAG, "Request has been aborted.");
+                return TextToSpeech.ERROR;
+            }
+            if (mAudioTrack == null) {
+                Log.e(TAG, "done(): Not started");
+                return TextToSpeech.ERROR;
+            }
+            cleanUp();
+        }
+        return TextToSpeech.SUCCESS;
+    }
+}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/SynthesisRequest.java b/core/java/android/speech/tts/SynthesisRequest.java
new file mode 100644
index 0000000..3f2ec5d
--- /dev/null
+++ b/core/java/android/speech/tts/SynthesisRequest.java
@@ -0,0 +1,151 @@
+/*
+ * 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 android.speech.tts;
+
+/**
+ * A request for speech synthesis given to a TTS engine for processing.
+ *
+ * @hide Pending approval
+ */
+public abstract class SynthesisRequest {
+
+    private final String mText;
+    private String mLanguage;
+    private String mCountry;
+    private String mVariant;
+    private int mSpeechRate;
+    private int mPitch;
+
+    public SynthesisRequest(String text) {
+        mText = text;
+    }
+
+    /**
+     * Sets the locale for the request.
+     */
+    void setLanguage(String language, String country, String variant) {
+        mLanguage = language;
+        mCountry = country;
+        mVariant = variant;
+    }
+
+    /**
+     * Sets the speech rate.
+     */
+    void setSpeechRate(int speechRate) {
+        mSpeechRate = speechRate;
+    }
+
+    /**
+     * Sets the pitch.
+     */
+    void setPitch(int pitch) {
+        mPitch = pitch;
+    }
+
+    /**
+     * Gets the text which should be synthesized.
+     */
+    public String getText() {
+        return mText;
+    }
+
+    /**
+     * Gets the ISO 3-letter language code for the language to use.
+     */
+    public String getLanguage() {
+        return mLanguage;
+    }
+
+    /**
+     * Gets the ISO 3-letter country code for the language to use.
+     */
+    public String getCountry() {
+        return mCountry;
+    }
+
+    /**
+     * Gets the language variant to use.
+     */
+    public String getVariant() {
+        return mVariant;
+    }
+
+    /**
+     * Gets the speech rate to use. {@link TextToSpeech.Engine#DEFAULT_RATE} (100)
+     * is the normal rate.
+     */
+    public int getSpeechRate() {
+        return mSpeechRate;
+    }
+
+    /**
+     * Gets the pitch to use. {@link TextToSpeech.Engine#DEFAULT_PITCH} (100)
+     * is the normal pitch.
+     */
+    public int getPitch() {
+        return mPitch;
+    }
+
+    /**
+     * Aborts the speech request.
+     *
+     * Can be called from multiple threads.
+     */
+    abstract void stop();
+
+    /**
+     * The service should call this when it starts to synthesize audio for this
+     * request.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @param sampleRateInHz Sample rate in HZ of the generated audio.
+     * @param audioFormat Audio format of the generated audio. Must be one of
+     *         the ENCODING_ constants defined in {@link android.media.AudioFormat}.
+     * @param channelCount The number of channels
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public abstract int start(int sampleRateInHz, int audioFormat, int channelCount);
+
+    /**
+     * The service should call this method when synthesized audio is ready for consumption.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @param buffer The generated audio data. This method will not hold on to {@code buffer},
+     *         so the caller is free to modify it after this method returns.
+     * @param offset The offset into {@code buffer} where the audio data starts.
+     * @param length The number of bytes of audio data in {@code buffer}.
+     *         Must be less than or equal to {@code buffer.length - offset}.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public abstract int audioAvailable(byte[] buffer, int offset, int length);
+
+    /**
+     * The service should call this method when all the synthesized audio for a request has
+     * been passed to {@link #audioAvailable}.
+     *
+     * This method should only be called on the synthesis thread,
+     * while in {@link TextToSpeechService#onSynthesizeText}.
+     *
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    public abstract int done();
+
+}
\ No newline at end of file
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 95830ec..2add7b5 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2009 Google Inc.
+ * Copyright (C) 2009 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
@@ -15,22 +15,31 @@
  */
 package android.speech.tts;
 
-import android.speech.tts.ITts;
-import android.speech.tts.ITtsCallback;
-
 import android.annotation.SdkConstant;
 import android.annotation.SdkConstant.SdkConstantType;
 import android.content.ComponentName;
+import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
 import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Bundle;
 import android.os.IBinder;
 import android.os.RemoteException;
+import android.provider.Settings;
+import android.text.TextUtils;
 import android.util.Log;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 
 /**
  *
@@ -49,11 +58,11 @@
     /**
      * Denotes a successful operation.
      */
-    public static final int SUCCESS                = 0;
+    public static final int SUCCESS = 0;
     /**
      * Denotes a generic operation failure.
      */
-    public static final int ERROR                  = -1;
+    public static final int ERROR = -1;
 
     /**
      * Queue mode where all entries in the playback queue (media to be played
@@ -65,20 +74,17 @@
      */
     public static final int QUEUE_ADD = 1;
 
-
     /**
      * Denotes the language is available exactly as specified by the locale.
      */
     public static final int LANG_COUNTRY_VAR_AVAILABLE = 2;
 
-
     /**
      * Denotes the language is available for the language and country specified 
      * by the locale, but not the variant.
      */
     public static final int LANG_COUNTRY_AVAILABLE = 1;
 
-
     /**
      * Denotes the language is available for the language by the locale, 
      * but not the country and variant.
@@ -95,7 +101,6 @@
      */
     public static final int LANG_NOT_SUPPORTED = -2;
 
-
     /**
      * Broadcast Action: The TextToSpeech synthesizer has completed processing
      * of all the text in the speech queue.
@@ -104,7 +109,6 @@
     public static final String ACTION_TTS_QUEUE_PROCESSING_COMPLETED =
             "android.speech.tts.TTS_QUEUE_PROCESSING_COMPLETED";
 
-
     /**
      * Interface definition of a callback to be invoked indicating the completion of the
      * TextToSpeech engine initialization.
@@ -112,103 +116,117 @@
     public interface OnInitListener {
         /**
          * Called to signal the completion of the TextToSpeech engine initialization.
+         *
          * @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
          */
         public void onInit(int status);
     }
 
     /**
-     * Interface definition of a callback to be invoked indicating the TextToSpeech engine has
-     * completed synthesizing an utterance with an utterance ID set.
-     *
+     * Listener that will be called when the TTS service has
+     * completed synthesizing an utterance. This is only called if the utterance
+     * has an utterance ID (see {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID}).
      */
     public interface OnUtteranceCompletedListener {
         /**
-         * Called to signal the completion of the synthesis of the utterance that was identified
-         * with the string parameter. This identifier is the one originally passed in the
-         * parameter hashmap of the synthesis request in
-         * {@link TextToSpeech#speak(String, int, HashMap)} or
-         * {@link TextToSpeech#synthesizeToFile(String, HashMap, String)} with the
-         * {@link TextToSpeech.Engine#KEY_PARAM_UTTERANCE_ID} key.
+         * Called when an utterance has been synthesized.
+         *
          * @param utteranceId the identifier of the utterance.
          */
         public void onUtteranceCompleted(String utteranceId);
     }
 
-
     /**
-     * Internal constants for the TextToSpeech functionality
-     *
+     * Constants and parameter names for controlling text-to-speech.
      */
     public class Engine {
-        // default values for a TTS engine when settings are not found in the provider
+
         /**
-         * {@hide}
+         * Default speech rate.
+         * @hide
          */
-        public static final int DEFAULT_RATE = 100; // 1x
+        public static final int DEFAULT_RATE = 100;
+
         /**
-         * {@hide}
+         * Default pitch.
+         * @hide
          */
-        public static final int DEFAULT_PITCH = 100;// 1x
+        public static final int DEFAULT_PITCH = 100;
+
         /**
-         * {@hide}
+         * Default volume.
+         * @hide
          */
         public static final float DEFAULT_VOLUME = 1.0f;
+
         /**
-         * {@hide}
-         */
-        protected static final String DEFAULT_VOLUME_STRING = "1.0";
-        /**
-         * {@hide}
+         * Default pan (centered).
+         * @hide
          */
         public static final float DEFAULT_PAN = 0.0f;
-        /**
-         * {@hide}
-         */
-        protected static final String DEFAULT_PAN_STRING = "0.0";
 
         /**
-         * {@hide}
+         * Default value for {@link Settings.Secure#TTS_USE_DEFAULTS}.
+         * @hide
          */
         public static final int USE_DEFAULTS = 0; // false
-        /**
-         * {@hide}
-         */
-        public static final String DEFAULT_SYNTH = "com.svox.pico";
 
-        // default values for rendering
+        /**
+         * Package name of the default TTS engine.
+         *
+         * TODO: This should come from a system property
+         *
+         * @hide
+         */
+        public static final String DEFAULT_ENGINE = "com.svox.pico";
+
         /**
          * Default audio stream used when playing synthesized speech.
          */
         public static final int DEFAULT_STREAM = AudioManager.STREAM_MUSIC;
 
-        // return codes for a TTS engine's check data activity
         /**
          * Indicates success when checking the installation status of the resources used by the
          * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_PASS = 1;
+
         /**
          * Indicates failure when checking the installation status of the resources used by the
          * TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_FAIL = 0;
+
         /**
          * Indicates erroneous data when checking the installation status of the resources used by
          * the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
+
         /**
          * Indicates missing resources when checking the installation status of the resources used
          * by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
+
         /**
          * Indicates missing storage volume when checking the installation status of the resources
          * used by the TextToSpeech engine with the {@link #ACTION_CHECK_TTS_DATA} intent.
          */
         public static final int CHECK_VOICE_DATA_MISSING_VOLUME = -3;
 
+        /**
+         * Intent for starting a TTS service. Services that handle this intent must
+         * extend {@link TextToSpeechService}. Normal applications should not use this intent
+         * directly, instead they should talk to the TTS service using the the methods in this
+         * class.
+         *
+         * @hide Pending API council approval
+         */
+        @SdkConstant(SdkConstantType.SERVICE_ACTION)
+        public static final String INTENT_ACTION_TTS_SERVICE =
+                "android.intent.action.TTS_SERVICE";
+
         // intents to ask engine to install data or check its data
         /**
          * Activity Action: Triggers the platform TextToSpeech engine to
@@ -231,6 +249,7 @@
         @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
         public static final String ACTION_TTS_DATA_INSTALLED =
                 "android.speech.tts.engine.TTS_DATA_INSTALLED";
+
         /**
          * Activity Action: Starts the activity from the platform TextToSpeech
          * engine to verify the proper installation and availability of the
@@ -258,23 +277,36 @@
         public static final String ACTION_CHECK_TTS_DATA =
                 "android.speech.tts.engine.CHECK_TTS_DATA";
 
+        /**
+         * Activity intent for getting some sample text to use for demonstrating TTS.
+         *
+         * @hide This intent was used by engines written against the old API.
+         * Not sure if it should be exposed.
+         */
+        @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+        public static final String ACTION_GET_SAMPLE_TEXT =
+                "android.speech.tts.engine.GET_SAMPLE_TEXT";
+
         // extras for a TTS engine's check data activity
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine specifies the path to its resources.
          */
         public static final String EXTRA_VOICE_DATA_ROOT_DIRECTORY = "dataRoot";
+
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine specifies the file names of its resources under the
          * resource path.
          */
         public static final String EXTRA_VOICE_DATA_FILES = "dataFiles";
+
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine specifies the locale associated with each resource file.
          */
         public static final String EXTRA_VOICE_DATA_FILES_INFO = "dataFilesInfo";
+
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine returns an ArrayList<String> of all the available voices.
@@ -282,6 +314,7 @@
          * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
          */
         public static final String EXTRA_AVAILABLE_VOICES = "availableVoices";
+
         /**
          * Extra information received with the {@link #ACTION_CHECK_TTS_DATA} intent where
          * the TextToSpeech engine returns an ArrayList<String> of all the unavailable voices.
@@ -289,6 +322,7 @@
          * optional (ie, "eng" or "eng-USA" or "eng-USA-FEMALE").
          */
         public static final String EXTRA_UNAVAILABLE_VOICES = "unavailableVoices";
+
         /**
          * Extra information sent with the {@link #ACTION_CHECK_TTS_DATA} intent where the
          * caller indicates to the TextToSpeech engine which specific sets of voice data to
@@ -311,134 +345,87 @@
         // keys for the parameters passed with speak commands. Hidden keys are used internally
         // to maintain engine state for each TextToSpeech instance.
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_RATE = "rate";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_LANGUAGE = "language";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_COUNTRY = "country";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_VARIANT = "variant";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_ENGINE = "engine";
+
         /**
-         * {@hide}
+         * @hide
          */
         public static final String KEY_PARAM_PITCH = "pitch";
+
         /**
          * Parameter key to specify the audio stream type to be used when speaking text
-         * or playing back a file.
+         * or playing back a file. The value should be one of the STREAM_ constants
+         * defined in {@link AudioManager}.
+         *
          * @see TextToSpeech#speak(String, int, HashMap)
          * @see TextToSpeech#playEarcon(String, int, HashMap)
          */
         public static final String KEY_PARAM_STREAM = "streamType";
+
         /**
          * Parameter key to identify an utterance in the
          * {@link TextToSpeech.OnUtteranceCompletedListener} after text has been
          * spoken, a file has been played back or a silence duration has elapsed.
+         *
          * @see TextToSpeech#speak(String, int, HashMap)
          * @see TextToSpeech#playEarcon(String, int, HashMap)
          * @see TextToSpeech#synthesizeToFile(String, HashMap, String)
          */
         public static final String KEY_PARAM_UTTERANCE_ID = "utteranceId";
+
         /**
          * Parameter key to specify the speech volume relative to the current stream type
          * volume used when speaking text. Volume is specified as a float ranging from 0 to 1
          * where 0 is silence, and 1 is the maximum volume (the default behavior).
+         *
          * @see TextToSpeech#speak(String, int, HashMap)
          * @see TextToSpeech#playEarcon(String, int, HashMap)
          */
         public static final String KEY_PARAM_VOLUME = "volume";
+
         /**
          * Parameter key to specify how the speech is panned from left to right when speaking text.
          * Pan is specified as a float ranging from -1 to +1 where -1 maps to a hard-left pan,
          * 0 to center (the default behavior), and +1 to hard-right.
+         *
          * @see TextToSpeech#speak(String, int, HashMap)
          * @see TextToSpeech#playEarcon(String, int, HashMap)
          */
         public static final String KEY_PARAM_PAN = "pan";
 
-        // key positions in the array of cached parameters
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_RATE = 0;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_LANGUAGE = 2;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_COUNTRY = 4;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_VARIANT = 6;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_STREAM = 8;
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_UTTERANCE_ID = 10;
-
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_ENGINE = 12;
-
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_PITCH = 14;
-
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_VOLUME = 16;
-
-        /**
-         * {@hide}
-         */
-        protected static final int PARAM_POSITION_PAN = 18;
-
-
-        /**
-         * {@hide}
-         * Total number of cached speech parameters.
-         * This number should be equal to (max param position/2) + 1.
-         */
-        protected static final int NB_CACHED_PARAMS = 10;
     }
 
-    /**
-     * Connection needed for the TTS.
-     */
-    private ServiceConnection mServiceConnection;
-
-    private ITts mITts = null;
-    private ITtsCallback mITtscallback = null;
-    private Context mContext = null;
-    private String mPackageName = "";
-    private OnInitListener mInitListener = null;
-    private boolean mStarted = false;
+    private final Context mContext;
+    private Connection mServiceConnection;
+    private OnInitListener mInitListener;
     private final Object mStartLock = new Object();
-    /**
-     * Used to store the cached parameters sent along with each synthesis request to the
-     * TTS service.
-     */
-    private String[] mCachedParams;
+
+    private String mRequestedEngine;
+    private final Map<String, Uri> mEarcons;
+    private final Map<String, Uri> mUtterances;
+    private final Bundle mParams = new Bundle();
 
     /**
      * The constructor for the TextToSpeech class.
@@ -451,84 +438,98 @@
      *            TextToSpeech engine has initialized.
      */
     public TextToSpeech(Context context, OnInitListener listener) {
+        this(context, listener, null);
+    }
+
+    /**
+     * @hide pending approval
+     */
+    public TextToSpeech(Context context, OnInitListener listener, String engine) {
         mContext = context;
-        mPackageName = mContext.getPackageName();
         mInitListener = listener;
+        mRequestedEngine = engine;
 
-        mCachedParams = new String[2*Engine.NB_CACHED_PARAMS]; // store key and value
-        mCachedParams[Engine.PARAM_POSITION_RATE] = Engine.KEY_PARAM_RATE;
-        mCachedParams[Engine.PARAM_POSITION_LANGUAGE] = Engine.KEY_PARAM_LANGUAGE;
-        mCachedParams[Engine.PARAM_POSITION_COUNTRY] = Engine.KEY_PARAM_COUNTRY;
-        mCachedParams[Engine.PARAM_POSITION_VARIANT] = Engine.KEY_PARAM_VARIANT;
-        mCachedParams[Engine.PARAM_POSITION_STREAM] = Engine.KEY_PARAM_STREAM;
-        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID] = Engine.KEY_PARAM_UTTERANCE_ID;
-        mCachedParams[Engine.PARAM_POSITION_ENGINE] = Engine.KEY_PARAM_ENGINE;
-        mCachedParams[Engine.PARAM_POSITION_PITCH] = Engine.KEY_PARAM_PITCH;
-        mCachedParams[Engine.PARAM_POSITION_VOLUME] = Engine.KEY_PARAM_VOLUME;
-        mCachedParams[Engine.PARAM_POSITION_PAN] = Engine.KEY_PARAM_PAN;
-
-        // Leave all defaults that are shown in Settings uninitialized/at the default
-        // so that the values set in Settings will take effect if the application does
-        // not try to change these settings itself.
-        mCachedParams[Engine.PARAM_POSITION_RATE + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
-                String.valueOf(Engine.DEFAULT_STREAM);
-        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_PITCH + 1] = "100";
-        mCachedParams[Engine.PARAM_POSITION_VOLUME + 1] = Engine.DEFAULT_VOLUME_STRING;
-        mCachedParams[Engine.PARAM_POSITION_PAN + 1] = Engine.DEFAULT_PAN_STRING;
+        mEarcons = new HashMap<String, Uri>();
+        mUtterances = new HashMap<String, Uri>();
 
         initTts();
     }
 
-
-    private void initTts() {
-        mStarted = false;
-
-        // Initialize the TTS, run the callback after the binding is successful
-        mServiceConnection = new ServiceConnection() {
-            public void onServiceConnected(ComponentName name, IBinder service) {
-                synchronized(mStartLock) {
-                    mITts = ITts.Stub.asInterface(service);
-                    mStarted = true;
-                    // Cache the default engine and current language
-                    setEngineByPackageName(getDefaultEngine());
-                    setLanguage(getLanguage());
-                    if (mInitListener != null) {
-                        // TODO manage failures and missing resources
-                        mInitListener.onInit(SUCCESS);
-                    }
-                }
-            }
-
-            public void onServiceDisconnected(ComponentName name) {
-                synchronized(mStartLock) {
-                    mITts = null;
-                    mInitListener = null;
-                    mStarted = false;
-                }
-            }
-        };
-
-        Intent intent = new Intent("android.intent.action.START_TTS_SERVICE");
-        intent.addCategory("android.intent.category.TTS");
-        boolean bound = mContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
-        if (!bound) {
-            Log.e("TextToSpeech.java", "initTts() failed to bind to service");
-            if (mInitListener != null) {
-                mInitListener.onInit(ERROR);
-            }
-        } else {
-            // initialization listener will be called inside ServiceConnection
-            Log.i("TextToSpeech.java", "initTts() successfully bound to service");
-        }
-        // TODO handle plugin failures
+    private String getPackageName() {
+        return mContext.getPackageName();
     }
 
+    private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) {
+        return runAction(action, errorResult, method, false);
+    }
+
+    private <R> R runAction(Action<R> action, R errorResult, String method) {
+        return runAction(action, errorResult, method, true);
+    }
+
+    private <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
+        synchronized (mStartLock) {
+            if (mServiceConnection == null) {
+                Log.w(TAG, method + " failed: not bound to TTS engine");
+                return errorResult;
+            }
+            return mServiceConnection.runAction(action, errorResult, method, reconnect);
+        }
+    }
+
+    private int initTts() {
+        String defaultEngine = getDefaultEngine();
+        String engine = defaultEngine;
+        if (!areDefaultsEnforced() && !TextUtils.isEmpty(mRequestedEngine)
+                && isEngineEnabled(engine)) {
+            engine = mRequestedEngine;
+        }
+
+        // Try requested engine
+        if (connectToEngine(engine)) {
+            return SUCCESS;
+        }
+
+        // Fall back to user's default engine if different from the already tested one
+        if (!engine.equals(defaultEngine)) {
+            if (connectToEngine(defaultEngine)) {
+                return SUCCESS;
+            }
+        }
+
+        // Fall back to the hardcoded default if different from the two above
+        if (!defaultEngine.equals(Engine.DEFAULT_ENGINE)
+                && !engine.equals(Engine.DEFAULT_ENGINE)) {
+            if (connectToEngine(Engine.DEFAULT_ENGINE)) {
+                return SUCCESS;
+            }
+        }
+
+        return ERROR;
+    }
+
+    private boolean connectToEngine(String engine) {
+        Connection connection = new Connection();
+        Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+        intent.setPackage(engine);
+        boolean bound = mContext.bindService(intent, connection, Context.BIND_AUTO_CREATE);
+        if (!bound) {
+            Log.e(TAG, "Failed to bind to " + engine);
+            dispatchOnInit(ERROR);
+            return false;
+        } else {
+            return true;
+        }
+    }
+
+    private void dispatchOnInit(int result) {
+        synchronized (mStartLock) {
+            if (mInitListener != null) {
+                mInitListener.onInit(result);
+                mInitListener = null;
+            }
+        }
+    }
 
     /**
      * Releases the resources used by the TextToSpeech engine.
@@ -536,15 +537,17 @@
      * so the TextToSpeech engine can be cleanly stopped.
      */
     public void shutdown() {
-        try {
-            mContext.unbindService(mServiceConnection);
-        } catch (IllegalArgumentException e) {
-            // Do nothing and fail silently since an error here indicates that
-            // binding never succeeded in the first place.
-        }
+        runActionNoReconnect(new Action<Void>() {
+            @Override
+            public Void run(ITextToSpeechService service) throws RemoteException {
+                service.setCallback(getPackageName(), null);
+                service.stop(getPackageName());
+                mServiceConnection.disconnect();
+                return null;
+            }
+        }, null, "shutdown");
     }
 
-
     /**
      * Adds a mapping between a string of text and a sound resource in a
      * package. After a call to this method, subsequent calls to
@@ -573,21 +576,9 @@
      * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
      */
     public int addSpeech(String text, String packagename, int resourceId) {
-        synchronized(mStartLock) {
-            if (!mStarted) {
-                return ERROR;
-            }
-            try {
-                mITts.addSpeech(mPackageName, text, packagename, resourceId);
-                return SUCCESS;
-            } catch (RemoteException e) {
-                restart("addSpeech", e);
-            } catch (NullPointerException e) {
-                restart("addSpeech", e);
-            } catch (IllegalStateException e) {
-                restart("addSpeech", e);
-            }
-            return ERROR;
+        synchronized (mStartLock) {
+            mUtterances.put(text, makeResourceUri(packagename, resourceId));
+            return SUCCESS;
         }
     }
 
@@ -608,20 +599,8 @@
      */
     public int addSpeech(String text, String filename) {
         synchronized (mStartLock) {
-            if (!mStarted) {
-                return ERROR;
-            }
-            try {
-                mITts.addSpeechFile(mPackageName, text, filename);
-                return SUCCESS;
-            } catch (RemoteException e) {
-                restart("addSpeech", e);
-            } catch (NullPointerException e) {
-                restart("addSpeech", e);
-            } catch (IllegalStateException e) {
-                restart("addSpeech", e);
-            }
-            return ERROR;
+            mUtterances.put(text, Uri.parse(filename));
+            return SUCCESS;
         }
     }
 
@@ -653,24 +632,11 @@
      */
     public int addEarcon(String earcon, String packagename, int resourceId) {
         synchronized(mStartLock) {
-            if (!mStarted) {
-                return ERROR;
-            }
-            try {
-                mITts.addEarcon(mPackageName, earcon, packagename, resourceId);
-                return SUCCESS;
-            } catch (RemoteException e) {
-                restart("addEarcon", e);
-            } catch (NullPointerException e) {
-                restart("addEarcon", e);
-            } catch (IllegalStateException e) {
-                restart("addEarcon", e);
-            }
-            return ERROR;
+            mEarcons.put(earcon, makeResourceUri(packagename, resourceId));
+            return SUCCESS;
         }
     }
 
-
     /**
      * Adds a mapping between a string of text and a sound file.
      * Use this to add custom earcons.
@@ -687,312 +653,199 @@
      * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
      */
     public int addEarcon(String earcon, String filename) {
-        synchronized (mStartLock) {
-            if (!mStarted) {
-                return ERROR;
-            }
-            try {
-                mITts.addEarconFile(mPackageName, earcon, filename);
-                return SUCCESS;
-            } catch (RemoteException e) {
-                restart("addEarcon", e);
-            } catch (NullPointerException e) {
-                restart("addEarcon", e);
-            } catch (IllegalStateException e) {
-                restart("addEarcon", e);
-            }
-            return ERROR;
+        synchronized(mStartLock) {
+            mEarcons.put(earcon, Uri.parse(filename));
+            return SUCCESS;
         }
     }
 
+    private Uri makeResourceUri(String packageName, int resourceId) {
+        return new Uri.Builder()
+                .scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
+                .encodedAuthority(packageName)
+                .appendEncodedPath(String.valueOf(resourceId))
+                .build();
+    }
 
     /**
      * Speaks the string using the specified queuing strategy and speech
      * parameters.
      *
-     * @param text
-     *            The string of text to be spoken.
-     * @param queueMode
-     *            The queuing strategy to use.
-     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
-     * @param params
-     *            The list of parameters to be used. Can be null if no parameters are given.
-     *            They are specified using a (key, value) pair, where the key can be
-     *            {@link Engine#KEY_PARAM_STREAM} or
-     *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
+     * @param text The string of text to be spoken.
+     * @param queueMode The queuing strategy to use, {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_STREAM},
+     *            {@link Engine#KEY_PARAM_UTTERANCE_ID},
+     *            {@link Engine#KEY_PARAM_VOLUME},
+     *            {@link Engine#KEY_PARAM_PAN}.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int speak(String text, int queueMode, HashMap<String,String> params)
-    {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            Log.i("TextToSpeech.java - speak", "speak text of length " + text.length());
-            if (!mStarted) {
-                Log.e("TextToSpeech.java - speak", "service isn't started");
-                return result;
-            }
-            try {
-                if ((params != null) && (!params.isEmpty())) {
-                    setCachedParam(params, Engine.KEY_PARAM_STREAM, Engine.PARAM_POSITION_STREAM);
-                    setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
-                            Engine.PARAM_POSITION_UTTERANCE_ID);
-                    setCachedParam(params, Engine.KEY_PARAM_ENGINE, Engine.PARAM_POSITION_ENGINE);
-                    setCachedParam(params, Engine.KEY_PARAM_VOLUME, Engine.PARAM_POSITION_VOLUME);
-                    setCachedParam(params, Engine.KEY_PARAM_PAN, Engine.PARAM_POSITION_PAN);
+    public int speak(final String text, final int queueMode, final HashMap<String, String> params) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                Uri utteranceUri = mUtterances.get(text);
+                if (utteranceUri != null) {
+                    return service.playAudio(getPackageName(), utteranceUri, queueMode,
+                            getParams(params));
+                } else {
+                    return service.speak(getPackageName(), text, queueMode, getParams(params));
                 }
-                result = mITts.speak(mPackageName, text, queueMode, mCachedParams);
-            } catch (RemoteException e) {
-                restart("speak", e);
-            } catch (NullPointerException e) {
-                restart("speak", e);
-            } catch (IllegalStateException e) {
-                restart("speak", e);
-            } finally {
-                resetCachedParams();
             }
-            return result;
-        }
+        }, ERROR, "speak");
     }
 
-
     /**
      * Plays the earcon using the specified queueing mode and parameters.
+     * The earcon must already have been added with {@link #addEarcon(String, String)} or
+     * {@link #addEarcon(String, String, int)}.
      *
-     * @param earcon
-     *            The earcon that should be played
-     * @param queueMode
-     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
-     * @param params
-     *            The list of parameters to be used. Can be null if no parameters are given.
-     *            They are specified using a (key, value) pair, where the key can be
-     *            {@link Engine#KEY_PARAM_STREAM} or
+     * @param earcon The earcon that should be played
+     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
+     *            {@link Engine#KEY_PARAM_STREAM},
      *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int playEarcon(String earcon, int queueMode,
-            HashMap<String,String> params) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            try {
-                if ((params != null) && (!params.isEmpty())) {
-                    String extra = params.get(Engine.KEY_PARAM_STREAM);
-                    if (extra != null) {
-                        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] = extra;
-                    }
-                    setCachedParam(params, Engine.KEY_PARAM_STREAM, Engine.PARAM_POSITION_STREAM);
-                    setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
-                            Engine.PARAM_POSITION_UTTERANCE_ID);
+    public int playEarcon(final String earcon, final int queueMode,
+            final HashMap<String, String> params) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                Uri earconUri = mEarcons.get(earcon);
+                if (earconUri == null) {
+                    return ERROR;
                 }
-                result = mITts.playEarcon(mPackageName, earcon, queueMode, null);
-            } catch (RemoteException e) {
-                restart("playEarcon", e);
-            } catch (NullPointerException e) {
-                restart("playEarcon", e);
-            } catch (IllegalStateException e) {
-                restart("playEarcon", e);
-            } finally {
-                resetCachedParams();
+                return service.playAudio(getPackageName(), earconUri, queueMode,
+                        getParams(params));
             }
-            return result;
-        }
+        }, ERROR, "playEarcon");
     }
 
     /**
      * Plays silence for the specified amount of time using the specified
      * queue mode.
      *
-     * @param durationInMs
-     *            A long that indicates how long the silence should last.
-     * @param queueMode
-     *            {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
-     * @param params
-     *            The list of parameters to be used. Can be null if no parameters are given.
-     *            They are specified using a (key, value) pair, where the key can be
+     * @param durationInMs The duration of the silence.
+     * @param queueMode {@link #QUEUE_ADD} or {@link #QUEUE_FLUSH}.
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
      *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int playSilence(long durationInMs, int queueMode, HashMap<String,String> params) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
+    public int playSilence(final long durationInMs, final int queueMode,
+            final HashMap<String, String> params) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                return service.playSilence(getPackageName(), durationInMs, queueMode,
+                        getParams(params));
             }
-            try {
-                if ((params != null) && (!params.isEmpty())) {
-                    setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
-                            Engine.PARAM_POSITION_UTTERANCE_ID);
-                }
-                result = mITts.playSilence(mPackageName, durationInMs, queueMode, mCachedParams);
-            } catch (RemoteException e) {
-                restart("playSilence", e);
-            } catch (NullPointerException e) {
-                restart("playSilence", e);
-            } catch (IllegalStateException e) {
-                restart("playSilence", e);
-            } finally {
-                resetCachedParams();
-            }
-            return result;
-        }
+        }, ERROR, "playSilence");
     }
 
-
     /**
-     * Returns whether or not the TextToSpeech engine is busy speaking.
+     * Checks whether the TTS engine is busy speaking.
      *
-     * @return Whether or not the TextToSpeech engine is busy speaking.
+     * @return {@code true} if the TTS engine is speaking.
      */
     public boolean isSpeaking() {
-        synchronized (mStartLock) {
-            if (!mStarted) {
-                return false;
+        return runAction(new Action<Boolean>() {
+            @Override
+            public Boolean run(ITextToSpeechService service) throws RemoteException {
+                return service.isSpeaking();
             }
-            try {
-                return mITts.isSpeaking();
-            } catch (RemoteException e) {
-                restart("isSpeaking", e);
-            } catch (NullPointerException e) {
-                restart("isSpeaking", e);
-            } catch (IllegalStateException e) {
-                restart("isSpeaking", e);
-            }
-            return false;
-        }
+        }, false, "isSpeaking");
     }
 
-
     /**
      * Interrupts the current utterance (whether played or rendered to file) and discards other
      * utterances in the queue.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
     public int stop() {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                return service.stop(getPackageName());
             }
-            try {
-                result = mITts.stop(mPackageName);
-            } catch (RemoteException e) {
-                restart("stop", e);
-            } catch (NullPointerException e) {
-                restart("stop", e);
-            } catch (IllegalStateException e) {
-                restart("stop", e);
-            }
-            return result;
-        }
+        }, ERROR, "stop");
     }
 
-
     /**
-     * Sets the speech rate for the TextToSpeech engine.
+     * Sets the speech rate.
      *
      * This has no effect on any pre-recorded speech.
      *
-     * @param speechRate
-     *            The speech rate for the TextToSpeech engine. 1 is the normal speed,
-     *            lower values slow down the speech (0.5 is half the normal speech rate),
-     *            greater values accelerate it (2 is twice the normal speech rate).
+     * @param speechRate Speech rate. {@code 1.0} is the normal speech rate,
+     *            lower values slow down the speech ({@code 0.5} is half the normal speech rate),
+     *            greater values accelerate it ({@code 2.0} is twice the normal speech rate).
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
     public int setSpeechRate(float speechRate) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            try {
-                if (speechRate > 0) {
-                    int rate = (int)(speechRate*100);
-                    mCachedParams[Engine.PARAM_POSITION_RATE + 1] = String.valueOf(rate);
-                    // the rate is not set here, instead it is cached so it will be associated
-                    // with all upcoming utterances.
-                    if (speechRate > 0.0f) {
-                        result = SUCCESS;
-                    } else {
-                        result = ERROR;
-                    }
+        if (speechRate > 0.0f) {
+            int intRate = (int)(speechRate * 100);
+            if (intRate > 0) {
+                synchronized (mStartLock) {
+                    mParams.putInt(Engine.KEY_PARAM_RATE, intRate);
                 }
-            } catch (NullPointerException e) {
-                restart("setSpeechRate", e);
-            } catch (IllegalStateException e) {
-                restart("setSpeechRate", e);
+                return SUCCESS;
             }
-            return result;
         }
+        return ERROR;
     }
 
-
     /**
      * Sets the speech pitch for the TextToSpeech engine.
      *
      * This has no effect on any pre-recorded speech.
      *
-     * @param pitch
-     *            The pitch for the TextToSpeech engine. 1 is the normal pitch,
+     * @param pitch Speech pitch. {@code 1.0} is the normal pitch,
      *            lower values lower the tone of the synthesized voice,
      *            greater values increase it.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
     public int setPitch(float pitch) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            try {
-                // the pitch is not set here, instead it is cached so it will be associated
-                // with all upcoming utterances.
-                if (pitch > 0) {
-                    int p = (int)(pitch*100);
-                    mCachedParams[Engine.PARAM_POSITION_PITCH + 1] = String.valueOf(p);
-                    result = SUCCESS;
+        if (pitch > 0.0f) {
+            int intPitch = (int)(pitch * 100);
+            if (intPitch > 0) {
+                synchronized (mStartLock) {
+                    mParams.putInt(Engine.KEY_PARAM_PITCH, intPitch);
                 }
-            } catch (NullPointerException e) {
-                restart("setPitch", e);
-            } catch (IllegalStateException e) {
-                restart("setPitch", e);
+                return SUCCESS;
             }
-            return result;
         }
+        return ERROR;
     }
 
-
     /**
-     * Sets the language for the TextToSpeech engine.
-     * The TextToSpeech engine will try to use the closest match to the specified
+     * Sets the text-to-speech language.
+     * The TTS engine will try to use the closest match to the specified
      * language as represented by the Locale, but there is no guarantee that the exact same Locale
      * will be used. Use {@link #isLanguageAvailable(Locale)} to check the level of support
      * before choosing the language to use for the next utterances.
      *
-     * @param loc
-     *            The locale describing the language to be used.
+     * @param loc The locale describing the language to be used.
      *
-     * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
+     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
      *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
      *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
      */
-    public int setLanguage(Locale loc) {
-        synchronized (mStartLock) {
-            int result = LANG_NOT_SUPPORTED;
-            if (!mStarted) {
-                return result;
-            }
-            if (loc == null) {
-                return result;
-            }
-            try {
+    public int setLanguage(final Locale loc) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                if (loc == null) {
+                    return LANG_NOT_SUPPORTED;
+                }
                 String language = loc.getISO3Language();
                 String country = loc.getISO3Country();
                 String variant = loc.getVariant();
@@ -1000,294 +853,316 @@
                 // the available parts.
                 // Note that the language is not actually set here, instead it is cached so it
                 // will be associated with all upcoming utterances.
-                result = mITts.isLanguageAvailable(language, country, variant, mCachedParams);
+                int result = service.loadLanguage(language, country, variant);
                 if (result >= LANG_AVAILABLE){
-                    mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1] = language;
-                    if (result >= LANG_COUNTRY_AVAILABLE){
-                        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = country;
-                    } else {
-                        mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1] = "";
+                    if (result < LANG_COUNTRY_VAR_AVAILABLE) {
+                        variant = "";
+                        if (result < LANG_COUNTRY_AVAILABLE) {
+                            country = "";
+                        }
                     }
-                    if (result >= LANG_COUNTRY_VAR_AVAILABLE){
-                        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = variant;
-                    } else {
-                        mCachedParams[Engine.PARAM_POSITION_VARIANT + 1] = "";
-                    }
+                    mParams.putString(Engine.KEY_PARAM_LANGUAGE, language);
+                    mParams.putString(Engine.KEY_PARAM_COUNTRY, country);
+                    mParams.putString(Engine.KEY_PARAM_VARIANT, variant);
                 }
-            } catch (RemoteException e) {
-                restart("setLanguage", e);
-            } catch (NullPointerException e) {
-                restart("setLanguage", e);
-            } catch (IllegalStateException e) {
-                restart("setLanguage", e);
+                return result;
             }
-            return result;
-        }
+        }, LANG_NOT_SUPPORTED, "setLanguage");
     }
 
-
     /**
      * Returns a Locale instance describing the language currently being used by the TextToSpeech
      * engine.
+     *
      * @return language, country (if any) and variant (if any) used by the engine stored in a Locale
-     *     instance, or null is the TextToSpeech engine has failed.
+     *     instance, or {@code null} on error.
      */
     public Locale getLanguage() {
-        synchronized (mStartLock) {
-            if (!mStarted) {
+        return runAction(new Action<Locale>() {
+            @Override
+            public Locale run(ITextToSpeechService service) throws RemoteException {
+                String[] locStrings = service.getLanguage();
+                if (locStrings != null && locStrings.length == 3) {
+                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
+                }
                 return null;
             }
-            try {
-                // Only do a call to the native synth if there is nothing in the cached params
-                if (mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1].length() < 1){
-                    String[] locStrings = mITts.getLanguage();
-                    if ((locStrings != null) && (locStrings.length == 3)) {
-                        return new Locale(locStrings[0], locStrings[1], locStrings[2]);
-                    } else {
-                        return null;
-                    }
-                } else {
-                    return new Locale(mCachedParams[Engine.PARAM_POSITION_LANGUAGE + 1],
-                            mCachedParams[Engine.PARAM_POSITION_COUNTRY + 1],
-                            mCachedParams[Engine.PARAM_POSITION_VARIANT + 1]);
-                }
-            } catch (RemoteException e) {
-                restart("getLanguage", e);
-            } catch (NullPointerException e) {
-                restart("getLanguage", e);
-            } catch (IllegalStateException e) {
-                restart("getLanguage", e);
-            }
-            return null;
-        }
+        }, null, "getLanguage");
     }
 
     /**
      * Checks if the specified language as represented by the Locale is available and supported.
      *
-     * @param loc
-     *            The Locale describing the language to be used.
+     * @param loc The Locale describing the language to be used.
      *
-     * @return code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
+     * @return Code indicating the support status for the locale. See {@link #LANG_AVAILABLE},
      *         {@link #LANG_COUNTRY_AVAILABLE}, {@link #LANG_COUNTRY_VAR_AVAILABLE},
      *         {@link #LANG_MISSING_DATA} and {@link #LANG_NOT_SUPPORTED}.
      */
-    public int isLanguageAvailable(Locale loc) {
-        synchronized (mStartLock) {
-            int result = LANG_NOT_SUPPORTED;
-            if (!mStarted) {
-                return result;
+    public int isLanguageAvailable(final Locale loc) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                return service.isLanguageAvailable(loc.getISO3Language(),
+                        loc.getISO3Country(), loc.getVariant());
             }
-            try {
-                result = mITts.isLanguageAvailable(loc.getISO3Language(),
-                        loc.getISO3Country(), loc.getVariant(), mCachedParams);
-            } catch (RemoteException e) {
-                restart("isLanguageAvailable", e);
-            } catch (NullPointerException e) {
-                restart("isLanguageAvailable", e);
-            } catch (IllegalStateException e) {
-                restart("isLanguageAvailable", e);
-            }
-            return result;
-        }
+        }, LANG_NOT_SUPPORTED, "isLanguageAvailable");
     }
 
-
     /**
      * Synthesizes the given text to a file using the specified parameters.
      *
-     * @param text
-     *            The String of text that should be synthesized
-     * @param params
-     *            The list of parameters to be used. Can be null if no parameters are given.
-     *            They are specified using a (key, value) pair, where the key can be
+     * @param text Thetext that should be synthesized
+     * @param params Parameters for the request. Can be null.
+     *            Supported parameter names:
      *            {@link Engine#KEY_PARAM_UTTERANCE_ID}.
-     * @param filename
-     *            The string that gives the full output filename; it should be
+     * @param filename Absolute file filename to write the generated audio data to.It should be
      *            something like "/sdcard/myappsounds/mysound.wav".
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int synthesizeToFile(String text, HashMap<String,String> params,
-            String filename) {
-        Log.i("TextToSpeech.java", "synthesizeToFile()");
-        synchronized (mStartLock) {
-            int result = ERROR;
-            Log.i("TextToSpeech.java - synthesizeToFile", "synthesizeToFile text of length "
-                    + text.length());
-            if (!mStarted) {
-                Log.e("TextToSpeech.java - synthesizeToFile", "service isn't started");
-                return result;
+    public int synthesizeToFile(final String text, final HashMap<String, String> params,
+            final String filename) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                return service.synthesizeToFile(getPackageName(), text, filename,
+                        getParams(params));
             }
+        }, ERROR, "synthesizeToFile");
+    }
+
+    private Bundle getParams(HashMap<String, String> params) {
+        if (params != null && !params.isEmpty()) {
+            Bundle bundle = new Bundle(mParams);
+            copyIntParam(bundle, params, Engine.KEY_PARAM_STREAM);
+            copyStringParam(bundle, params, Engine.KEY_PARAM_UTTERANCE_ID);
+            copyFloatParam(bundle, params, Engine.KEY_PARAM_VOLUME);
+            copyFloatParam(bundle, params, Engine.KEY_PARAM_PAN);
+            return bundle;
+        } else {
+            return mParams;
+        }
+    }
+
+    private void copyStringParam(Bundle bundle, HashMap<String, String> params, String key) {
+        String value = params.get(key);
+        if (value != null) {
+            bundle.putString(key, value);
+        }
+    }
+
+    private void copyIntParam(Bundle bundle, HashMap<String, String> params, String key) {
+        String valueString = params.get(key);
+        if (!TextUtils.isEmpty(valueString)) {
             try {
-                if ((params != null) && (!params.isEmpty())) {
-                    // no need to read the stream type here
-                    setCachedParam(params, Engine.KEY_PARAM_UTTERANCE_ID,
-                            Engine.PARAM_POSITION_UTTERANCE_ID);
-                    setCachedParam(params, Engine.KEY_PARAM_ENGINE, Engine.PARAM_POSITION_ENGINE);
-                }
-                result = mITts.synthesizeToFile(mPackageName, text, mCachedParams, filename) ?
-                        SUCCESS : ERROR;
-            } catch (RemoteException e) {
-                restart("synthesizeToFile", e);
-            } catch (NullPointerException e) {
-                restart("synthesizeToFile", e);
-            } catch (IllegalStateException e) {
-                restart("synthesizeToFile", e);
-            } finally {
-                resetCachedParams();
+                int value = Integer.parseInt(valueString);
+                bundle.putInt(key, value);
+            } catch (NumberFormatException ex) {
+                // don't set the value in the bundle
             }
-            return result;
         }
     }
 
-
-    /**
-     * Convenience method to reset the cached parameters to the current default values
-     * if they are not persistent between calls to the service.
-     */
-    private void resetCachedParams() {
-        mCachedParams[Engine.PARAM_POSITION_STREAM + 1] =
-                String.valueOf(Engine.DEFAULT_STREAM);
-        mCachedParams[Engine.PARAM_POSITION_UTTERANCE_ID+ 1] = "";
-        mCachedParams[Engine.PARAM_POSITION_VOLUME + 1] = Engine.DEFAULT_VOLUME_STRING;
-        mCachedParams[Engine.PARAM_POSITION_PAN + 1] = Engine.DEFAULT_PAN_STRING;
-    }
-
-    /**
-     * Convenience method to save a parameter in the cached parameter array, at the given index,
-     * for a property saved in the given hashmap.
-     */
-    private void setCachedParam(HashMap<String,String> params, String key, int keyIndex) {
-        String extra = params.get(key);
-        if (extra != null) {
-            mCachedParams[keyIndex+1] = extra;
+    private void copyFloatParam(Bundle bundle, HashMap<String, String> params, String key) {
+        String valueString = params.get(key);
+        if (!TextUtils.isEmpty(valueString)) {
+            try {
+                float value = Float.parseFloat(valueString);
+                bundle.putFloat(key, value);
+            } catch (NumberFormatException ex) {
+                // don't set the value in the bundle
+            }
         }
     }
 
     /**
-     * Sets the OnUtteranceCompletedListener that will fire when an utterance completes.
+     * Sets the listener that will be notified when synthesis of an utterance completes.
      *
-     * @param listener
-     *            The OnUtteranceCompletedListener
+     * @param listener The listener to use.
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
-    public int setOnUtteranceCompletedListener(
-            final OnUtteranceCompletedListener listener) {
-        synchronized (mStartLock) {
-            int result = ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            mITtscallback = new ITtsCallback.Stub() {
-                public void utteranceCompleted(String utteranceId) throws RemoteException {
-                    if (listener != null) {
-                        listener.onUtteranceCompleted(utteranceId);
+    public int setOnUtteranceCompletedListener(final OnUtteranceCompletedListener listener) {
+        return runAction(new Action<Integer>() {
+            @Override
+            public Integer run(ITextToSpeechService service) throws RemoteException {
+                ITextToSpeechCallback.Stub callback = new ITextToSpeechCallback.Stub() {
+                    public void utteranceCompleted(String utteranceId) {
+                        if (listener != null) {
+                            listener.onUtteranceCompleted(utteranceId);
+                        }
                     }
-                }
-            };
-            try {
-                result = mITts.registerCallback(mPackageName, mITtscallback);
-            } catch (RemoteException e) {
-                restart("registerCallback", e);
-            } catch (NullPointerException e) {
-                restart("registerCallback", e);
-            } catch (IllegalStateException e) {
-                restart("registerCallback", e);
+                };
+                service.setCallback(getPackageName(), callback);
+                return SUCCESS;
             }
-            return result;
-        }
+        }, ERROR, "setOnUtteranceCompletedListener");
     }
 
     /**
-     * Sets the speech synthesis engine to be used by its packagename.
+     * Sets the TTS engine to use.
      *
-     * @param enginePackageName
-     *            The packagename for the synthesis engine (ie, "com.svox.pico")
+     * @param enginePackageName The package name for the synthesis engine (e.g. "com.svox.pico")
      *
-     * @return Code indicating success or failure. See {@link #ERROR} and {@link #SUCCESS}.
+     * @return {@link #ERROR} or {@link #SUCCESS}.
      */
+    // TODO: add @Deprecated{This method does not tell the caller when the new engine
+    // has been initialized. You should create a new TextToSpeech object with the new
+    // engine instead.}
     public int setEngineByPackageName(String enginePackageName) {
-        synchronized (mStartLock) {
-            int result = TextToSpeech.ERROR;
-            if (!mStarted) {
-                return result;
-            }
-            try {
-                result = mITts.setEngineByPackageName(enginePackageName);
-                if (result == TextToSpeech.SUCCESS){
-                    mCachedParams[Engine.PARAM_POSITION_ENGINE + 1] = enginePackageName;
-                }
-            } catch (RemoteException e) {
-                restart("setEngineByPackageName", e);
-            } catch (NullPointerException e) {
-                restart("setEngineByPackageName", e);
-            } catch (IllegalStateException e) {
-                restart("setEngineByPackageName", e);
-            }
-            return result;
-        }
+        mRequestedEngine = enginePackageName;
+        return initTts();
     }
 
-
     /**
-     * Gets the packagename of the default speech synthesis engine.
+     * Gets the package name of the default speech synthesis engine.
      *
-     * @return Packagename of the TTS engine that the user has chosen as their default.
+     * @return Package name of the TTS engine that the user has chosen as their default.
      */
     public String getDefaultEngine() {
-        synchronized (mStartLock) {
-            String engineName = "";
-            if (!mStarted) {
-                return engineName;
-            }
-            try {
-                engineName = mITts.getDefaultEngine();
-            } catch (RemoteException e) {
-                restart("getDefaultEngine", e);
-            } catch (NullPointerException e) {
-                restart("getDefaultEngine", e);
-            } catch (IllegalStateException e) {
-                restart("getDefaultEngine", e);
-            }
-            return engineName;
-        }
+        String engine = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.TTS_DEFAULT_SYNTH);
+        return engine != null ? engine : Engine.DEFAULT_ENGINE;
     }
 
-
     /**
-     * Returns whether or not the user is forcing their defaults to override the
-     * Text-To-Speech settings set by applications.
-     *
-     * @return Whether or not defaults are enforced.
+     * Checks whether the user's settings should override settings requested by the calling
+     * application.
      */
     public boolean areDefaultsEnforced() {
-        synchronized (mStartLock) {
-            boolean defaultsEnforced = false;
-            if (!mStarted) {
-                return defaultsEnforced;
-            }
-            try {
-                defaultsEnforced = mITts.areDefaultsEnforced();
-            } catch (RemoteException e) {
-                restart("areDefaultsEnforced", e);
-            } catch (NullPointerException e) {
-                restart("areDefaultsEnforced", e);
-            } catch (IllegalStateException e) {
-                restart("areDefaultsEnforced", e);
-            }
-            return defaultsEnforced;
+        return Settings.Secure.getInt(mContext.getContentResolver(),
+                Settings.Secure.TTS_USE_DEFAULTS, Engine.USE_DEFAULTS) == 1;
+    }
+
+    private boolean isEngineEnabled(String engine) {
+        if (Engine.DEFAULT_ENGINE.equals(engine)) {
+            return true;
         }
+        for (String enabled : getEnabledEngines()) {
+            if (engine.equals(enabled)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private String[] getEnabledEngines() {
+        String str = Settings.Secure.getString(mContext.getContentResolver(),
+                Settings.Secure.TTS_ENABLED_PLUGINS);
+        if (TextUtils.isEmpty(str)) {
+            return new String[0];
+        }
+        return str.split(" ");
     }
 
     /**
-     * Restarts the TTS after a failure.
+     * Gets a list of all installed TTS engines.
+     *
+     * @return A list of engine info objects. The list can be empty, but will never by {@code null}.
+     *
+     * @hide Pending approval
      */
-    private void restart(String method, Exception e) {
-        // TTS died; restart it.
-        Log.e(TAG, method, e);
-        mStarted = false;
-        initTts();
+    public List<EngineInfo> getEngines() {
+        PackageManager pm = mContext.getPackageManager();
+        Intent intent = new Intent(Engine.INTENT_ACTION_TTS_SERVICE);
+        List<ResolveInfo> resolveInfos = pm.queryIntentServices(intent, 0);
+        if (resolveInfos == null) return Collections.emptyList();
+        List<EngineInfo> engines = new ArrayList<EngineInfo>(resolveInfos.size());
+        for (ResolveInfo resolveInfo : resolveInfos) {
+            ServiceInfo service = resolveInfo.serviceInfo;
+            if (service != null) {
+                EngineInfo engine = new EngineInfo();
+                // Using just the package name isn't great, since it disallows having
+                // multiple engines in the same package, but that's what the existing API does.
+                engine.name = service.packageName;
+                CharSequence label = service.loadLabel(pm);
+                engine.label = TextUtils.isEmpty(label) ? engine.name : label.toString();
+                engine.icon = service.getIconResource();
+                engines.add(engine);
+            }
+        }
+        return engines;
+    }
+
+    private class Connection implements ServiceConnection {
+        private ITextToSpeechService mService;
+
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            Log.i(TAG, "Connected to " + name);
+            synchronized(mStartLock) {
+                if (mServiceConnection != null) {
+                    // Disconnect any previous service connection
+                    mServiceConnection.disconnect();
+                }
+                mServiceConnection = this;
+                mService = ITextToSpeechService.Stub.asInterface(service);
+                dispatchOnInit(SUCCESS);
+            }
+        }
+
+        public void onServiceDisconnected(ComponentName name) {
+            synchronized(mStartLock) {
+                mService = null;
+                // If this is the active connection, clear it
+                if (mServiceConnection == this) {
+                    mServiceConnection = null;
+                }
+            }
+        }
+
+        public void disconnect() {
+            mContext.unbindService(this);
+        }
+
+        public <R> R runAction(Action<R> action, R errorResult, String method, boolean reconnect) {
+            try {
+                synchronized (mStartLock) {
+                    if (mService == null) {
+                        Log.w(TAG, method + " failed: not connected to TTS engine");
+                        return errorResult;
+                    }
+                    return action.run(mService);
+                }
+            } catch (RemoteException ex) {
+                Log.e(TAG, method + " failed", ex);
+                if (reconnect) {
+                    disconnect();
+                    initTts();
+                }
+                return errorResult;
+            }
+        }
+    }
+
+    private interface Action<R> {
+        R run(ITextToSpeechService service) throws RemoteException;
+    }
+
+    /**
+     * Information about an installed text-to-speech engine.
+     *
+     * @see TextToSpeech#getEngines
+     * @hide Pending approval
+     */
+    public static class EngineInfo {
+        /**
+         * Engine package name..
+         */
+        public String name;
+        /**
+         * Localized label for the engine.
+         */
+        public String label;
+        /**
+         * Icon for the engine.
+         */
+        public int icon;
+
+        @Override
+        public String toString() {
+            return "EngineInfo{name=" + name + "}";
+        }
+
     }
 }
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
new file mode 100644
index 0000000..a408ea2
--- /dev/null
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -0,0 +1,715 @@
+/*
+ * 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 android.speech.tts;
+
+import android.app.Service;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.ConditionVariable;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.MessageQueue;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.provider.Settings;
+import android.speech.tts.TextToSpeech.Engine;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Locale;
+
+
+/**
+ * Abstract base class for TTS engine implementations.
+ *
+ * @hide Pending approval
+ */
+public abstract class TextToSpeechService extends Service {
+
+    private static final boolean DBG = false;
+    private static final String TAG = "TextToSpeechService";
+
+    private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
+    private static final String SYNTH_THREAD_NAME = "SynthThread";
+
+    private SynthHandler mSynthHandler;
+
+    private CallbackMap mCallbacks;
+
+    @Override
+    public void onCreate() {
+        if (DBG) Log.d(TAG, "onCreate()");
+        super.onCreate();
+
+        SynthThread synthThread = new SynthThread();
+        synthThread.start();
+        mSynthHandler = new SynthHandler(synthThread.getLooper());
+
+        mCallbacks = new CallbackMap();
+
+        // Load default language
+        onLoadLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+    }
+
+    @Override
+    public void onDestroy() {
+        if (DBG) Log.d(TAG, "onDestroy()");
+
+        // Tell the synthesizer to stop
+        mSynthHandler.quit();
+
+        // Unregister all callbacks.
+        mCallbacks.kill();
+
+        super.onDestroy();
+    }
+
+    /**
+     * Checks whether the engine supports a given language.
+     *
+     * Can be called on multiple threads.
+     *
+     * @param lang ISO-3 language code.
+     * @param country ISO-3 country code. May be empty or null.
+     * @param variant Language variant. May be empty or null.
+     * @return Code indicating the support status for the locale.
+     *         One of {@link TextToSpeech#LANG_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+     *         {@link TextToSpeech#LANG_MISSING_DATA}
+     *         {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+     */
+    protected abstract int onIsLanguageAvailable(String lang, String country, String variant);
+
+    /**
+     * Returns the language, country and variant currently being used by the TTS engine.
+     *
+     * Can be called on multiple threads.
+     *
+     * @return A 3-element array, containing language (ISO 3-letter code),
+     *         country (ISO 3-letter code) and variant used by the engine.
+     *         The country and variant may be {@code ""}. If country is empty, then variant must
+     *         be empty too.
+     * @see Locale#getISO3Language()
+     * @see Locale#getISO3Country()
+     * @see Locale#getVariant()
+     */
+    protected abstract String[] onGetLanguage();
+
+    /**
+     * Notifies the engine that it should load a speech synthesis language. There is no guarantee
+     * that this method is always called before the language is used for synthesis. It is merely
+     * a hint to the engine that it will probably get some synthesis requests for this language
+     * at some point in the future.
+     *
+     * Can be called on multiple threads.
+     *
+     * @param lang ISO-3 language code.
+     * @param country ISO-3 country code. May be empty or null.
+     * @param variant Language variant. May be empty or null.
+     * @return Code indicating the support status for the locale.
+     *         One of {@link TextToSpeech#LANG_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_AVAILABLE},
+     *         {@link TextToSpeech#LANG_COUNTRY_VAR_AVAILABLE},
+     *         {@link TextToSpeech#LANG_MISSING_DATA}
+     *         {@link TextToSpeech#LANG_NOT_SUPPORTED}.
+     */
+    protected abstract int onLoadLanguage(String lang, String country, String variant);
+
+    /**
+     * Notifies the service that it should stop any in-progress speech synthesis.
+     * This method can be called even if no speech synthesis is currently in progress.
+     *
+     * Can be called on multiple threads, but not on the synthesis thread.
+     */
+    protected abstract void onStop();
+
+    /**
+     * Tells the service to synthesize speech from the given text. This method should
+     * block until the synthesis is finished.
+     *
+     * Called on the synthesis thread.
+     *
+     * @param request The synthesis request. The method should
+     *         call {@link SynthesisRequest#start}, {@link SynthesisRequest#audioAvailable},
+     *         and {@link SynthesisRequest#done} on this request.
+     * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+     */
+    protected abstract int onSynthesizeText(SynthesisRequest request);
+
+    private boolean areDefaultsEnforced() {
+        return getSecureSettingInt(Settings.Secure.TTS_USE_DEFAULTS,
+                TextToSpeech.Engine.USE_DEFAULTS) == 1;
+    }
+
+    private int getDefaultSpeechRate() {
+        return getSecureSettingInt(Settings.Secure.TTS_DEFAULT_RATE, Engine.DEFAULT_RATE);
+    }
+
+    private String getDefaultLanguage() {
+        return getSecureSettingString(Settings.Secure.TTS_DEFAULT_LANG,
+                Locale.getDefault().getISO3Language());
+    }
+
+    private String getDefaultCountry() {
+        return getSecureSettingString(Settings.Secure.TTS_DEFAULT_COUNTRY,
+                Locale.getDefault().getISO3Country());
+    }
+
+    private String getDefaultVariant() {
+        return getSecureSettingString(Settings.Secure.TTS_DEFAULT_VARIANT,
+                Locale.getDefault().getVariant());
+    }
+
+    private int getSecureSettingInt(String name, int defaultValue) {
+        return Settings.Secure.getInt(getContentResolver(), name, defaultValue);
+    }
+
+    private String getSecureSettingString(String name, String defaultValue) {
+        String value = Settings.Secure.getString(getContentResolver(), name);
+        return value != null ? value : defaultValue;
+    }
+
+    /**
+     * Synthesizer thread. This thread is used to run {@link SynthHandler}.
+     */
+    private class SynthThread extends HandlerThread implements MessageQueue.IdleHandler {
+
+        private boolean mFirstIdle = true;
+
+        public SynthThread() {
+            super(SYNTH_THREAD_NAME, android.os.Process.THREAD_PRIORITY_AUDIO);
+        }
+
+        @Override
+        protected void onLooperPrepared() {
+            getLooper().getQueue().addIdleHandler(this);
+        }
+
+        @Override
+        public boolean queueIdle() {
+            if (mFirstIdle) {
+                mFirstIdle = false;
+            } else {
+                broadcastTtsQueueProcessingCompleted();
+            }
+            return true;
+        }
+
+        private void broadcastTtsQueueProcessingCompleted() {
+            Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
+            if (DBG) Log.d(TAG, "Broadcasting: " + i);
+            sendBroadcast(i);
+        }
+    }
+
+    private class SynthHandler extends Handler {
+
+        private SpeechItem mCurrentSpeechItem = null;
+
+        public SynthHandler(Looper looper) {
+            super(looper);
+        }
+
+        private void dispatchUtteranceCompleted(SpeechItem item) {
+            String utteranceId = item.getUtteranceId();
+            if (!TextUtils.isEmpty(utteranceId)) {
+                mCallbacks.dispatchUtteranceCompleted(item.getCallingApp(), utteranceId);
+            }
+        }
+
+        private synchronized SpeechItem getCurrentSpeechItem() {
+            return mCurrentSpeechItem;
+        }
+
+        private synchronized SpeechItem setCurrentSpeechItem(SpeechItem speechItem) {
+            SpeechItem old = mCurrentSpeechItem;
+            mCurrentSpeechItem = speechItem;
+            return old;
+        }
+
+        public boolean isSpeaking() {
+            return getCurrentSpeechItem() != null;
+        }
+
+        public void quit() {
+            // Don't process any more speech items
+            getLooper().quit();
+            // Stop the current speech item
+            SpeechItem current = setCurrentSpeechItem(null);
+            if (current != null) {
+                current.stop();
+            }
+        }
+
+        /**
+         * Adds a speech item to the queue.
+         *
+         * Called on a service binder thread.
+         */
+        public int enqueueSpeechItem(int queueMode, final SpeechItem speechItem) {
+            if (!speechItem.isValid()) {
+                return TextToSpeech.ERROR;
+            }
+            // TODO: The old code also supported the undocumented queueMode == 2,
+            // which clears out all pending items from the calling app, as well as all
+            // non-file items from other apps.
+            if (queueMode == TextToSpeech.QUEUE_FLUSH) {
+                stop(speechItem.getCallingApp());
+            }
+            Runnable runnable = new Runnable() {
+                @Override
+                public void run() {
+                    setCurrentSpeechItem(speechItem);
+                    if (speechItem.play() == TextToSpeech.SUCCESS) {
+                        dispatchUtteranceCompleted(speechItem);
+                    }
+                    setCurrentSpeechItem(null);
+                }
+            };
+            Message msg = Message.obtain(this, runnable);
+            // The obj is used to remove all callbacks from the given app in stop(String).
+            msg.obj = speechItem.getCallingApp();
+            if (sendMessage(msg)) {
+                return TextToSpeech.SUCCESS;
+            } else {
+                Log.w(TAG, "SynthThread has quit");
+                return TextToSpeech.ERROR;
+            }
+        }
+
+        /**
+         * Stops all speech output and removes any utterances still in the queue for
+         * the calling app.
+         *
+         * Called on a service binder thread.
+         */
+        public int stop(String callingApp) {
+            if (TextUtils.isEmpty(callingApp)) {
+                return TextToSpeech.ERROR;
+            }
+            removeCallbacksAndMessages(callingApp);
+            SpeechItem current = setCurrentSpeechItem(null);
+            if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
+                current.stop();
+            }
+            return TextToSpeech.SUCCESS;
+        }
+    }
+
+    /**
+     * An item in the synth thread queue.
+     */
+    private static abstract class SpeechItem {
+        private final String mCallingApp;
+        private final Bundle mParams;
+        private boolean mStarted = false;
+        private boolean mStopped = false;
+
+        public SpeechItem(String callingApp, Bundle params) {
+            mCallingApp = callingApp;
+            mParams = params;
+        }
+
+        public String getCallingApp() {
+            return mCallingApp;
+        }
+
+        /**
+         * Checker whether the item is valid. If this method returns false, the item should not
+         * be played.
+         */
+        public abstract boolean isValid();
+
+        /**
+         * Plays the speech item. Blocks until playback is finished.
+         * Must not be called more than once.
+         *
+         * Only called on the synthesis thread.
+         *
+         * @return {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
+         */
+        public int play() {
+            synchronized (this) {
+                if (mStarted) {
+                    throw new IllegalStateException("play() called twice");
+                }
+                mStarted = true;
+            }
+            return playImpl();
+        }
+
+        /**
+         * Stops the speech item.
+         * Must not be called more than once.
+         *
+         * Can be called on multiple threads,  but not on the synthesis thread.
+         */
+        public void stop() {
+            synchronized (this) {
+                if (mStopped) {
+                    throw new IllegalStateException("stop() called twice");
+                }
+                mStopped = true;
+            }
+            stopImpl();
+        }
+
+        protected abstract int playImpl();
+
+        protected abstract void stopImpl();
+
+        public int getStreamType() {
+            return getIntParam(Engine.KEY_PARAM_STREAM, Engine.DEFAULT_STREAM);
+        }
+
+        public float getVolume() {
+            return getFloatParam(Engine.KEY_PARAM_VOLUME, Engine.DEFAULT_VOLUME);
+        }
+
+        public float getPan() {
+            return getFloatParam(Engine.KEY_PARAM_PAN, Engine.DEFAULT_PAN);
+        }
+
+        public String getUtteranceId() {
+            return getStringParam(Engine.KEY_PARAM_UTTERANCE_ID, null);
+        }
+
+        protected String getStringParam(String key, String defaultValue) {
+            return mParams == null ? defaultValue : mParams.getString(key, defaultValue);
+        }
+
+        protected int getIntParam(String key, int defaultValue) {
+            return mParams == null ? defaultValue : mParams.getInt(key, defaultValue);
+        }
+
+        protected float getFloatParam(String key, float defaultValue) {
+            return mParams == null ? defaultValue : mParams.getFloat(key, defaultValue);
+        }
+    }
+
+    private class SynthesisSpeechItem extends SpeechItem {
+        private final String mText;
+        private SynthesisRequest mSynthesisRequest;
+
+        public SynthesisSpeechItem(String callingApp, Bundle params, String text) {
+            super(callingApp, params);
+            mText = text;
+        }
+
+        public String getText() {
+            return mText;
+        }
+
+        @Override
+        public boolean isValid() {
+            if (TextUtils.isEmpty(mText)) {
+                Log.w(TAG, "Got empty text");
+                return false;
+            }
+            if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
+                Log.w(TAG, "Text too long: " + mText.length() + " chars");
+                return false;
+            }
+            return true;
+        }
+
+        @Override
+        protected int playImpl() {
+            SynthesisRequest synthesisRequest;
+            synchronized (this) {
+                mSynthesisRequest = createSynthesisRequest();
+                synthesisRequest = mSynthesisRequest;
+            }
+            setRequestParams(synthesisRequest);
+            return TextToSpeechService.this.onSynthesizeText(synthesisRequest);
+        }
+
+        protected SynthesisRequest createSynthesisRequest() {
+            return new PlaybackSynthesisRequest(mText, getStreamType(), getVolume(), getPan());
+        }
+
+        private void setRequestParams(SynthesisRequest request) {
+            if (areDefaultsEnforced()) {
+                request.setLanguage(getDefaultLanguage(), getDefaultCountry(), getDefaultVariant());
+                request.setSpeechRate(getDefaultSpeechRate());
+            } else {
+                request.setLanguage(getLanguage(), getCountry(), getVariant());
+                request.setSpeechRate(getSpeechRate());
+            }
+            request.setPitch(getPitch());
+        }
+
+        @Override
+        protected void stopImpl() {
+            SynthesisRequest synthesisRequest;
+            synchronized (this) {
+                synthesisRequest = mSynthesisRequest;
+            }
+            synthesisRequest.stop();
+            TextToSpeechService.this.onStop();
+        }
+
+        public String getLanguage() {
+            return getStringParam(Engine.KEY_PARAM_LANGUAGE, getDefaultLanguage());
+        }
+
+        private boolean hasLanguage() {
+            return !TextUtils.isEmpty(getStringParam(Engine.KEY_PARAM_LANGUAGE, null));
+        }
+
+        private String getCountry() {
+            if (!hasLanguage()) return getDefaultCountry();
+            return getStringParam(Engine.KEY_PARAM_COUNTRY, "");
+        }
+
+        private String getVariant() {
+            if (!hasLanguage()) return getDefaultVariant();
+            return getStringParam(Engine.KEY_PARAM_VARIANT, "");
+        }
+
+        private int getSpeechRate() {
+            return getIntParam(Engine.KEY_PARAM_RATE, getDefaultSpeechRate());
+        }
+
+        private int getPitch() {
+            return getIntParam(Engine.KEY_PARAM_PITCH, Engine.DEFAULT_PITCH);
+        }
+    }
+
+    private class SynthesisToFileSpeechItem extends SynthesisSpeechItem {
+        private final File mFile;
+
+        public SynthesisToFileSpeechItem(String callingApp, Bundle params, String text,
+                File file) {
+            super(callingApp, params, text);
+            mFile = file;
+        }
+
+        @Override
+        public boolean isValid() {
+            if (!super.isValid()) {
+                return false;
+            }
+            return checkFile(mFile);
+        }
+
+        @Override
+        protected SynthesisRequest createSynthesisRequest() {
+            return new FileSynthesisRequest(getText(), mFile);
+        }
+
+        /**
+         * Checks that the given file can be used for synthesis output.
+         */
+        private boolean checkFile(File file) {
+            try {
+                if (file.exists()) {
+                    Log.v(TAG, "File " + file + " exists, deleting.");
+                    if (!file.delete()) {
+                        Log.e(TAG, "Failed to delete " + file);
+                        return false;
+                    }
+                }
+                if (!file.createNewFile()) {
+                    Log.e(TAG, "Can't create file " + file);
+                    return false;
+                }
+                if (!file.delete()) {
+                    Log.e(TAG, "Failed to delete " + file);
+                    return false;
+                }
+                return true;
+            } catch (IOException e) {
+                Log.e(TAG, "Can't use " + file + " due to exception " + e);
+                return false;
+            }
+        }
+    }
+
+    private class AudioSpeechItem extends SpeechItem {
+
+        private final BlockingMediaPlayer mPlayer;
+
+        public AudioSpeechItem(String callingApp, Bundle params, Uri uri) {
+            super(callingApp, params);
+            mPlayer = new BlockingMediaPlayer(TextToSpeechService.this, uri, getStreamType());
+        }
+
+        @Override
+        public boolean isValid() {
+            return true;
+        }
+
+        @Override
+        protected int playImpl() {
+            return mPlayer.startAndWait() ? TextToSpeech.SUCCESS : TextToSpeech.ERROR;
+        }
+
+        @Override
+        protected void stopImpl() {
+            mPlayer.stop();
+        }
+    }
+
+    private class SilenceSpeechItem extends SpeechItem {
+        private final long mDuration;
+        private final ConditionVariable mDone;
+
+        public SilenceSpeechItem(String callingApp, Bundle params, long duration) {
+            super(callingApp, params);
+            mDuration = duration;
+            mDone = new ConditionVariable();
+        }
+
+        @Override
+        public boolean isValid() {
+            return true;
+        }
+
+        @Override
+        protected int playImpl() {
+            boolean aborted = mDone.block(mDuration);
+            return aborted ? TextToSpeech.ERROR : TextToSpeech.SUCCESS;
+        }
+
+        @Override
+        protected void stopImpl() {
+            mDone.open();
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        if (TextToSpeech.Engine.INTENT_ACTION_TTS_SERVICE.equals(intent.getAction())) {
+            return mBinder;
+        }
+        return null;
+    }
+
+    /**
+     * Binder returned from {@code #onBind(Intent)}. The methods in this class can be
+     * called called from several different threads.
+     */
+    private final ITextToSpeechService.Stub mBinder = new ITextToSpeechService.Stub() {
+
+        public int speak(String callingApp, String text, int queueMode, Bundle params) {
+            SpeechItem item = new SynthesisSpeechItem(callingApp, params, text);
+            return mSynthHandler.enqueueSpeechItem(queueMode, item);
+        }
+
+        public int synthesizeToFile(String callingApp, String text, String filename,
+                Bundle params) {
+            File file = new File(filename);
+            SpeechItem item = new SynthesisToFileSpeechItem(callingApp, params, text, file);
+            return mSynthHandler.enqueueSpeechItem(TextToSpeech.QUEUE_ADD, item);
+        }
+
+        public int playAudio(String callingApp, Uri audioUri, int queueMode, Bundle params) {
+            SpeechItem item = new AudioSpeechItem(callingApp, params, audioUri);
+            return mSynthHandler.enqueueSpeechItem(queueMode, item);
+        }
+
+        public int playSilence(String callingApp, long duration, int queueMode, Bundle params) {
+            SpeechItem item = new SilenceSpeechItem(callingApp, params, duration);
+            return mSynthHandler.enqueueSpeechItem(queueMode, item);
+        }
+
+        public boolean isSpeaking() {
+            return mSynthHandler.isSpeaking();
+        }
+
+        public int stop(String callingApp) {
+            return mSynthHandler.stop(callingApp);
+        }
+
+        public String[] getLanguage() {
+            return onGetLanguage();
+        }
+
+        public int isLanguageAvailable(String lang, String country, String variant) {
+            return onIsLanguageAvailable(lang, country, variant);
+        }
+
+        public int loadLanguage(String lang, String country, String variant) {
+            return onLoadLanguage(lang, country, variant);
+        }
+
+        public void setCallback(String packageName, ITextToSpeechCallback cb) {
+            mCallbacks.setCallback(packageName, cb);
+        }
+    };
+
+    private class CallbackMap extends RemoteCallbackList<ITextToSpeechCallback> {
+
+        private final HashMap<String, ITextToSpeechCallback> mAppToCallback
+                = new HashMap<String, ITextToSpeechCallback>();
+
+        public void setCallback(String packageName, ITextToSpeechCallback cb) {
+            synchronized (mAppToCallback) {
+                ITextToSpeechCallback old;
+                if (cb != null) {
+                    register(cb, packageName);
+                    old = mAppToCallback.put(packageName, cb);
+                } else {
+                    old = mAppToCallback.remove(packageName);
+                }
+                if (old != null && old != cb) {
+                    unregister(old);
+                }
+            }
+        }
+
+        public void dispatchUtteranceCompleted(String packageName, String utteranceId) {
+            ITextToSpeechCallback cb;
+            synchronized (mAppToCallback) {
+                cb = mAppToCallback.get(packageName);
+            }
+            if (cb == null) return;
+            try {
+                cb.utteranceCompleted(utteranceId);
+            } catch (RemoteException e) {
+                Log.e(TAG, "Callback failed: " + e);
+            }
+        }
+
+        @Override
+        public void onCallbackDied(ITextToSpeechCallback callback, Object cookie) {
+            String packageName = (String) cookie;
+            synchronized (mAppToCallback) {
+                mAppToCallback.remove(packageName);
+            }
+            mSynthHandler.stop(packageName);
+        }
+
+        @Override
+        public void kill() {
+            synchronized (mAppToCallback) {
+                mAppToCallback.clear();
+                super.kill();
+            }
+        }
+
+    }
+
+}
diff --git a/include/tts/TtsEngine.h b/include/tts/TtsEngine.h
deleted file mode 100644
index 998e353..0000000
--- a/include/tts/TtsEngine.h
+++ /dev/null
@@ -1,232 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * 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.
- */
-#include <media/AudioSystem.h>
-
-// This header defines the interface used by the Android platform
-// to access Text-To-Speech functionality in shared libraries that implement
-// speech synthesis and the management of resources associated with the
-// synthesis.
-// An example of the implementation of this interface can be found in
-// FIXME: add path+name to implementation of default TTS engine
-// Libraries implementing this interface are used in:
-//  frameworks/base/tts/jni/android_tts_SpeechSynthesis.cpp
-
-namespace android {
-
-#define ANDROID_TTS_ENGINE_PROPERTY_CONFIG "engineConfig"
-#define ANDROID_TTS_ENGINE_PROPERTY_PITCH  "pitch"
-#define ANDROID_TTS_ENGINE_PROPERTY_RATE   "rate"
-#define ANDROID_TTS_ENGINE_PROPERTY_VOLUME "volume"
-
-
-enum tts_synth_status {
-    TTS_SYNTH_DONE              = 0,
-    TTS_SYNTH_PENDING           = 1
-};
-
-enum tts_callback_status {
-    TTS_CALLBACK_HALT           = 0,
-    TTS_CALLBACK_CONTINUE       = 1
-};
-
-// The callback is used by the implementation of this interface to notify its
-// client, the Android TTS service, that the last requested synthesis has been
-// completed. // TODO reword
-// The callback for synthesis completed takes:
-// @param [inout] void *&       - The userdata pointer set in the original
-//                                 synth call
-// @param [in]    uint32_t      - Track sampling rate in Hz
-// @param [in]    uint32_t      - The audio format
-// @param [in]    int           - The number of channels
-// @param [inout] int8_t *&     - A buffer of audio data only valid during the
-//                                execution of the callback
-// @param [inout] size_t  &     - The size of the buffer
-// @param [in] tts_synth_status - indicate whether the synthesis is done, or
-//                                 if more data is to be synthesized.
-// @return TTS_CALLBACK_HALT to indicate the synthesis must stop,
-//         TTS_CALLBACK_CONTINUE to indicate the synthesis must continue if
-//            there is more data to produce.
-typedef tts_callback_status (synthDoneCB_t)(void *&, uint32_t,
-        uint32_t, int, int8_t *&, size_t&, tts_synth_status);
-
-class TtsEngine;
-extern "C" TtsEngine* getTtsEngine();
-
-enum tts_result {
-    TTS_SUCCESS                 = 0,
-    TTS_FAILURE                 = -1,
-    TTS_FEATURE_UNSUPPORTED     = -2,
-    TTS_VALUE_INVALID           = -3,
-    TTS_PROPERTY_UNSUPPORTED    = -4,
-    TTS_PROPERTY_SIZE_TOO_SMALL = -5,
-    TTS_MISSING_RESOURCES       = -6
-};
-
-enum tts_support_result {
-    TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
-    TTS_LANG_COUNTRY_AVAILABLE = 1,
-    TTS_LANG_AVAILABLE = 0,
-    TTS_LANG_MISSING_DATA = -1,
-    TTS_LANG_NOT_SUPPORTED = -2
-};
-
-class TtsEngine
-{
-public:
-    virtual ~TtsEngine() {}
-
-    // Initialize the TTS engine and returns whether initialization succeeded.
-    // @param synthDoneCBPtr synthesis callback function pointer
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result init(synthDoneCB_t synthDoneCBPtr, const char *engineConfig);
-
-    // Shut down the TTS engine and releases all associated resources.
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result shutdown();
-
-    // Interrupt synthesis and flushes any synthesized data that hasn't been
-    // output yet. This will block until callbacks underway are completed.
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result stop();
-
-    // Returns the level of support for the language, country and variant.
-    // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
-    //            and the corresponding resources are correctly installed
-    //         TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
-    //             corresponding resources are correctly installed, but there is no match for
-    //             the specified variant
-    //         TTS_LANG_AVAILABLE if the language is supported and the
-    //             corresponding resources are correctly installed, but there is no match for
-    //             the specified country and variant
-    //         TTS_LANG_MISSING_DATA if the required resources to provide any level of support
-    //             for the language are not correctly installed
-    //         TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
-    virtual tts_support_result isLanguageAvailable(const char *lang, const char *country,
-            const char *variant);
-
-    // Load the resources associated with the specified language. The loaded
-    // language will only be used once a call to setLanguage() with the same
-    // language value is issued. Language and country values are coded according to the ISO three
-    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
-    // instance. The variant value is encoded as the variant string retrieved from a
-    // java.util.Locale instance built with that variant data.
-    // @param lang pointer to the ISO three letter code for the language
-    // @param country pointer to the ISO three letter code for the country
-    // @param variant pointer to the variant code
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result loadLanguage(const char *lang, const char *country, const char *variant);
-
-    // Load the resources associated with the specified language, country and Locale variant.
-    // The loaded language will only be used once a call to setLanguageFromLocale() with the same
-    // language value is issued. Language and country values are coded according to the ISO three
-    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
-    // instance. The variant value is encoded as the variant string retrieved from a
-    // java.util.Locale instance built with that variant data.
-    // @param lang pointer to the ISO three letter code for the language
-    // @param country pointer to the ISO three letter code for the country
-    // @param variant pointer to the variant code
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result setLanguage(const char *lang, const char *country, const char *variant);
-
-    // Retrieve the currently set language, country and variant, or empty strings if none of
-    // parameters have been set. Language and country are represented by their 3-letter ISO code
-    // @param[out]   pointer to the retrieved 3-letter code language value
-    // @param[out]   pointer to the retrieved 3-letter code country value
-    // @param[out]   pointer to the retrieved variant value
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result getLanguage(char *language, char *country, char *variant);
-
-    // Notifies the engine what audio parameters should be used for the synthesis.
-    // This is meant to be used as a hint, the engine implementation will set the output values
-    // to those of the synthesis format, based on a given hint.
-    // @param[inout] encoding in: the desired audio sample format
-    //                         out: the format used by the TTS engine
-    // @param[inout] rate in: the desired audio sample rate
-    //                         out: the sample rate used by the TTS engine
-    // @param[inout] channels in: the desired number of audio channels
-    //                         out: the number of channels used by the TTS engine
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    virtual tts_result setAudioFormat(AudioSystem::audio_format& encoding, uint32_t& rate,
-            int& channels);
-
-    // Set a property for the the TTS engine
-    // "size" is the maximum size of "value" for properties "property"
-    // @param property pointer to the property name
-    // @param value    pointer to the property value
-    // @param size     maximum size required to store this type of property
-    // @return         TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_FAILURE,
-    //                  or TTS_VALUE_INVALID
-    virtual tts_result setProperty(const char *property, const char *value,
-            const size_t size);
-
-    // Retrieve a property from the TTS engine
-    // @param        property pointer to the property name
-    // @param[out]   value    pointer to the retrieved language value
-    // @param[inout] iosize   in: stores the size available to store the
-    //                          property value.
-    //                        out: stores the size required to hold the language
-    //                          value if getLanguage() returned
-    //                          TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise
-    // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS,
-    //         or TTS_PROPERTY_SIZE_TOO_SMALL
-    virtual tts_result getProperty(const char *property, char *value,
-            size_t *iosize);
-
-    // Synthesize the text.
-    // As the synthesis is performed, the engine invokes the callback to notify
-    // the TTS framework that it has filled the given buffer, and indicates how
-    // many bytes it wrote. The callback is called repeatedly until the engine
-    // has generated all the audio data corresponding to the text.
-    // Note about the format of the input: the text parameter may use the
-    // following elements
-    // and their respective attributes as defined in the SSML 1.0 specification:
-    //    * lang
-    //    * say-as:
-    //          o interpret-as
-    //    * phoneme
-    //    * voice:
-    //          o gender,
-    //          o age,
-    //          o variant,
-    //          o name
-    //    * emphasis
-    //    * break:
-    //          o strength,
-    //          o time
-    //    * prosody:
-    //          o pitch,
-    //          o contour,
-    //          o range,
-    //          o rate,
-    //          o duration,
-    //          o volume
-    //    * mark
-    // Differences between this text format and SSML are:
-    //    * full SSML documents are not supported
-    //    * namespaces are not supported
-    // Text is coded in UTF-8.
-    // @param text      the UTF-8 text to synthesize
-    // @param userdata  pointer to be returned when the call is invoked
-    // @param buffer    the location where the synthesized data must be written
-    // @param bufferSize the number of bytes that can be written in buffer
-    // @return          TTS_SUCCESS or TTS_FAILURE
-    virtual tts_result synthesizeText(const char *text, int8_t *buffer,
-            size_t bufferSize, void *userdata);
-
-};
-
-} // namespace android
-
diff --git a/native/include/android/tts.h b/native/include/android/tts.h
deleted file mode 100644
index fb15108..0000000
--- a/native/include/android/tts.h
+++ /dev/null
@@ -1,313 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * 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.
- */
-#ifndef ANDROID_TTS_H
-#define ANDROID_TTS_H 
-
-// This header defines the interface used by the Android platform
-// to access Text-To-Speech functionality in shared libraries that implement
-// speech synthesis and the management of resources associated with the
-// synthesis.
-
-// The shared library must contain a function named "android_getTtsEngine"
-// that returns an 'android_tts_engine_t' instance.
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define ANDROID_TTS_ENGINE_PROPERTY_CONFIG "engineConfig"
-#define ANDROID_TTS_ENGINE_PROPERTY_PITCH  "pitch"
-#define ANDROID_TTS_ENGINE_PROPERTY_RATE   "rate"
-#define ANDROID_TTS_ENGINE_PROPERTY_VOLUME "volume"
-
-typedef enum {
-    ANDROID_TTS_SUCCESS                 = 0,
-    ANDROID_TTS_FAILURE                 = -1,
-    ANDROID_TTS_FEATURE_UNSUPPORTED     = -2,
-    ANDROID_TTS_VALUE_INVALID           = -3,
-    ANDROID_TTS_PROPERTY_UNSUPPORTED    = -4,
-    ANDROID_TTS_PROPERTY_SIZE_TOO_SMALL = -5,
-    ANDROID_TTS_MISSING_RESOURCES       = -6
-} android_tts_result_t;
-
-typedef enum {
-    ANDROID_TTS_LANG_COUNTRY_VAR_AVAILABLE = 2,
-    ANDROID_TTS_LANG_COUNTRY_AVAILABLE    = 1,
-    ANDROID_TTS_LANG_AVAILABLE            = 0,
-    ANDROID_TTS_LANG_MISSING_DATA         = -1,
-    ANDROID_TTS_LANG_NOT_SUPPORTED        = -2
-} android_tts_support_result_t;
-
-typedef enum {
-    ANDROID_TTS_SYNTH_DONE              = 0,
-    ANDROID_TTS_SYNTH_PENDING           = 1
-} android_tts_synth_status_t;
-
-typedef enum {
-    ANDROID_TTS_CALLBACK_HALT           = 0,
-    ANDROID_TTS_CALLBACK_CONTINUE       = 1
-} android_tts_callback_status_t;
-
-// Supported audio formats
-typedef enum {
-    ANDROID_TTS_AUDIO_FORMAT_INVALID    = -1,
-    ANDROID_TTS_AUDIO_FORMAT_DEFAULT    = 0,
-    ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT = 1,
-    ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT  = 2,
-} android_tts_audio_format_t;
-
-
-/* An android_tts_engine_t object can be anything, but must have,
- * as its first field, a pointer to a table of functions.
- *
- * See the full definition of struct android_tts_engine_t_funcs_t
- * below for details.
- */
-typedef struct android_tts_engine_funcs_t  android_tts_engine_funcs_t;
-
-typedef struct {
-    android_tts_engine_funcs_t *funcs;
-} android_tts_engine_t;
-
-/* This function must be located in the TTS Engine shared library
- * and must return the address of an android_tts_engine_t library.
- */
-extern android_tts_engine_t *android_getTtsEngine();
-
-/* Including the old version for legacy support (Froyo compatibility).
- * This should return the same thing as android_getTtsEngine.
- */
-extern "C" android_tts_engine_t *getTtsEngine();
-
-// A callback type used to notify the framework of new synthetized
-// audio samples, status will be SYNTH_DONE for the last sample of
-// the last request, of SYNTH_PENDING otherwise.
-//
-// This is passed by the framework to the engine through the
-// 'engine_init' function (see below).
-//
-// The callback for synthesis completed takes:
-// @param [inout] void *&       - The userdata pointer set in the original
-//                                 synth call
-// @param [in]    uint32_t      - Track sampling rate in Hz
-// @param [in]    uint32_t      - The audio format
-// @param [in]    int           - The number of channels
-// @param [inout] int8_t *&     - A buffer of audio data only valid during the
-//                                execution of the callback
-// @param [inout] size_t  &     - The size of the buffer
-// @param [in] tts_synth_status - indicate whether the synthesis is done, or
-//                                 if more data is to be synthesized.
-// @return TTS_CALLBACK_HALT to indicate the synthesis must stop,
-//         TTS_CALLBACK_CONTINUE to indicate the synthesis must continue if
-//            there is more data to produce.
-typedef android_tts_callback_status_t (*android_tts_synth_cb_t)
-            (void **pUserData,
-             uint32_t trackSamplingHz,
-             android_tts_audio_format_t audioFormat,
-             int channelCount,
-             int8_t **pAudioBuffer,
-             size_t *pBufferSize,
-             android_tts_synth_status_t status);
-
-
-// The table of function pointers that the android_tts_engine_t must point to.
-// Note that each of these functions will take a handle to the engine itself
-// as their first parameter.
-//
-
-struct android_tts_engine_funcs_t {
-    // reserved fields, ignored by the framework
-    // they must be placed here to ensure binary compatibility
-    // of legacy binary plugins.
-    void *reserved[2];
-
-    // Initialize the TTS engine and returns whether initialization succeeded.
-    // @param synthDoneCBPtr synthesis callback function pointer
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*init)
-            (void *engine,
-             android_tts_synth_cb_t synthDonePtr,
-             const char *engineConfig);
-
-    // Shut down the TTS engine and releases all associated resources.
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*shutdown)
-            (void *engine);
-
-    // Interrupt synthesis and flushes any synthesized data that hasn't been
-    // output yet. This will block until callbacks underway are completed.
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*stop)
-            (void *engine);
-
-    // Returns the level of support for the language, country and variant.
-    // @return TTS_LANG_COUNTRY_VAR_AVAILABLE if the language, country and variant are supported,
-    //            and the corresponding resources are correctly installed
-    //         TTS_LANG_COUNTRY_AVAILABLE if the language and country are supported and the
-    //             corresponding resources are correctly installed, but there is no match for
-    //             the specified variant
-    //         TTS_LANG_AVAILABLE if the language is supported and the
-    //             corresponding resources are correctly installed, but there is no match for
-    //             the specified country and variant
-    //         TTS_LANG_MISSING_DATA if the required resources to provide any level of support
-    //             for the language are not correctly installed
-    //         TTS_LANG_NOT_SUPPORTED if the language is not supported by the TTS engine.
-    android_tts_support_result_t (*isLanguageAvailable)
-            (void *engine,
-             const char *lang,
-             const char *country,
-             const char *variant);
-
-    // Load the resources associated with the specified language. The loaded
-    // language will only be used once a call to setLanguage() with the same
-    // language value is issued. Language and country values are coded according to the ISO three
-    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
-    // instance. The variant value is encoded as the variant string retrieved from a
-    // java.util.Locale instance built with that variant data.
-    // @param lang pointer to the ISO three letter code for the language
-    // @param country pointer to the ISO three letter code for the country
-    // @param variant pointer to the variant code
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*loadLanguage)
-            (void *engine,
-             const char *lang,
-             const char *country,
-             const char *variant);
-
-    // Load the resources associated with the specified language, country and Locale variant.
-    // The loaded language will only be used once a call to setLanguageFromLocale() with the same
-    // language value is issued. Language and country values are coded according to the ISO three
-    // letter codes for languages and countries, as can be retrieved from a java.util.Locale
-    // instance. The variant value is encoded as the variant string retrieved from a
-    // java.util.Locale instance built with that variant data.
-    // @param lang pointer to the ISO three letter code for the language
-    // @param country pointer to the ISO three letter code for the country
-    // @param variant pointer to the variant code
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*setLanguage)
-            (void *engine,
-             const char *lang,
-             const char *country,
-             const char *variant);
-
-    // Retrieve the currently set language, country and variant, or empty strings if none of
-    // parameters have been set. Language and country are represented by their 3-letter ISO code
-    // @param[out]   pointer to the retrieved 3-letter code language value
-    // @param[out]   pointer to the retrieved 3-letter code country value
-    // @param[out]   pointer to the retrieved variant value
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*getLanguage)
-            (void *engine,
-             char *language,
-             char *country,
-             char *variant);
-
-    // Notifies the engine what audio parameters should be used for the synthesis.
-    // This is meant to be used as a hint, the engine implementation will set the output values
-    // to those of the synthesis format, based on a given hint.
-    // @param[inout] encoding in: the desired audio sample format
-    //                         out: the format used by the TTS engine
-    // @param[inout] rate in: the desired audio sample rate
-    //                         out: the sample rate used by the TTS engine
-    // @param[inout] channels in: the desired number of audio channels
-    //                         out: the number of channels used by the TTS engine
-    // @return TTS_SUCCESS, or TTS_FAILURE
-    android_tts_result_t (*setAudioFormat)
-            (void *engine,
-             android_tts_audio_format_t* pEncoding,
-             uint32_t* pRate,
-             int* pChannels);
-
-    // Set a property for the the TTS engine
-    // "size" is the maximum size of "value" for properties "property"
-    // @param property pointer to the property name
-    // @param value    pointer to the property value
-    // @param size     maximum size required to store this type of property
-    // @return         TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS, or TTS_FAILURE,
-    //                  or TTS_VALUE_INVALID
-    android_tts_result_t (*setProperty)
-            (void *engine,
-             const char *property,
-             const char *value,
-             const size_t size);
-
-    // Retrieve a property from the TTS engine
-    // @param        property pointer to the property name
-    // @param[out]   value    pointer to the retrieved language value
-    // @param[inout] iosize   in: stores the size available to store the
-    //                          property value.
-    //                        out: stores the size required to hold the language
-    //                          value if getLanguage() returned
-    //                          TTS_PROPERTY_SIZE_TOO_SMALL, unchanged otherwise
-    // @return TTS_PROPERTY_UNSUPPORTED, or TTS_SUCCESS,
-    //         or TTS_PROPERTY_SIZE_TOO_SMALL
-    android_tts_result_t (*getProperty)
-            (void *engine,
-             const char *property,
-             char *value,
-             size_t *iosize);
-
-    // Synthesize the text.
-    // As the synthesis is performed, the engine invokes the callback to notify
-    // the TTS framework that it has filled the given buffer, and indicates how
-    // many bytes it wrote. The callback is called repeatedly until the engine
-    // has generated all the audio data corresponding to the text.
-    // Note about the format of the input: the text parameter may use the
-    // following elements
-    // and their respective attributes as defined in the SSML 1.0 specification:
-    //    * lang
-    //    * say-as:
-    //          o interpret-as
-    //    * phoneme
-    //    * voice:
-    //          o gender,
-    //          o age,
-    //          o variant,
-    //          o name
-    //    * emphasis
-    //    * break:
-    //          o strength,
-    //          o time
-    //    * prosody:
-    //          o pitch,
-    //          o contour,
-    //          o range,
-    //          o rate,
-    //          o duration,
-    //          o volume
-    //    * mark
-    // Differences between this text format and SSML are:
-    //    * full SSML documents are not supported
-    //    * namespaces are not supported
-    // Text is coded in UTF-8.
-    // @param text      the UTF-8 text to synthesize
-    // @param userdata  pointer to be returned when the call is invoked
-    // @param buffer    the location where the synthesized data must be written
-    // @param bufferSize the number of bytes that can be written in buffer
-    // @return          TTS_SUCCESS or TTS_FAILURE
-    android_tts_result_t (*synthesizeText)
-            (void *engine,
-             const char *text,
-             int8_t *buffer,
-             size_t bufferSize,
-             void *userdata);
-};
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* ANDROID_TTS_H */
diff --git a/packages/TtsService/Android.mk b/packages/TtsService/Android.mk
deleted file mode 100644
index a1a3b9f..0000000
--- a/packages/TtsService/Android.mk
+++ /dev/null
@@ -1,15 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := optional
-
-LOCAL_SRC_FILES := $(call all-subdir-java-files) \
-
-LOCAL_PACKAGE_NAME := TtsService
-LOCAL_CERTIFICATE := platform
-
-LOCAL_PROGUARD_FLAG_FILES := proguard.flags
-
-include $(BUILD_PACKAGE)
-
-include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/packages/TtsService/AndroidManifest.xml b/packages/TtsService/AndroidManifest.xml
deleted file mode 100755
index 46e0ad1..0000000
--- a/packages/TtsService/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="android.tts">
-    <application android:label="TTS Service"
-        android:icon="@drawable/ic_launcher_text_to_speech">
-        <service android:enabled="true"
-                 android:name=".TtsService"
-                 android:label="TTS Service">
-            <intent-filter>
-                <action android:name="android.intent.action.START_TTS_SERVICE"/>
-                <category android:name="android.intent.category.TTS"/>
-            </intent-filter>
-        </service>
-    </application>
-    <uses-permission android:name="android.permission.INTERNET"/>
-    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-</manifest>
diff --git a/packages/TtsService/MODULE_LICENSE_APACHE2 b/packages/TtsService/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/packages/TtsService/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/packages/TtsService/NOTICE b/packages/TtsService/NOTICE
deleted file mode 100644
index 64aaa8d..0000000
--- a/packages/TtsService/NOTICE
+++ /dev/null
@@ -1,190 +0,0 @@
-
-   Copyright (c) 2009, 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.
-
-   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.
-
-
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
diff --git a/packages/TtsService/jni/Android.mk b/packages/TtsService/jni/Android.mk
deleted file mode 100755
index 5dc0c30..0000000
--- a/packages/TtsService/jni/Android.mk
+++ /dev/null
@@ -1,30 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_SRC_FILES:= \
-	android_tts_SynthProxy.cpp
-
-LOCAL_C_INCLUDES += \
-	frameworks/base/native/include \
-	$(JNI_H_INCLUDE)
-
-LOCAL_SHARED_LIBRARIES := \
-	libandroid_runtime \
-	libnativehelper \
-	libmedia \
-	libutils \
-	libcutils
-
-ifeq ($(TARGET_SIMULATOR),true)
- LOCAL_LDLIBS += -ldl
-else
- LOCAL_SHARED_LIBRARIES += libdl
-endif
-
-
-LOCAL_MODULE:= libttssynthproxy
-
-LOCAL_ARM_MODE := arm
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/packages/TtsService/jni/android_tts_SynthProxy.cpp b/packages/TtsService/jni/android_tts_SynthProxy.cpp
deleted file mode 100644
index e00fa85..0000000
--- a/packages/TtsService/jni/android_tts_SynthProxy.cpp
+++ /dev/null
@@ -1,1071 +0,0 @@
-/*
- * Copyright (C) 2009-2010 Google Inc.
- *
- * 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.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-
-#define LOG_TAG "SynthProxyJNI"
-
-#include <utils/Log.h>
-#include <nativehelper/jni.h>
-#include <nativehelper/JNIHelp.h>
-#include <android_runtime/AndroidRuntime.h>
-#include <android/tts.h>
-#include <media/AudioTrack.h>
-#include <math.h>
-
-#include <dlfcn.h>
-
-#define DEFAULT_TTS_RATE        16000
-#define DEFAULT_TTS_FORMAT      AudioSystem::PCM_16_BIT
-#define DEFAULT_TTS_NB_CHANNELS 1
-#define DEFAULT_TTS_BUFFERSIZE  2048
-#define DEFAULT_TTS_STREAM_TYPE AudioSystem::MUSIC
-#define DEFAULT_VOLUME          1.0f
-
-// EQ + BOOST parameters
-#define FILTER_LOWSHELF_ATTENUATION -18.0f // in dB
-#define FILTER_TRANSITION_FREQ 1100.0f     // in Hz
-#define FILTER_SHELF_SLOPE 1.0f            // Q
-#define FILTER_GAIN 5.5f // linear gain
-
-#define USAGEMODE_PLAY_IMMEDIATELY 0
-#define USAGEMODE_WRITE_TO_FILE    1
-
-#define SYNTHPLAYSTATE_IS_STOPPED 0
-#define SYNTHPLAYSTATE_IS_PLAYING 1
-
-using namespace android;
-
-// ----------------------------------------------------------------------------
-struct fields_t {
-    jfieldID    synthProxyFieldJniData;
-    jmethodID   synthProxyMethodPost;
-};
-
-// structure to hold the data that is used each time the TTS engine has synthesized more data
-struct afterSynthData_t {
-    jint jniStorage;
-    int  usageMode;
-    FILE* outputFile;
-    AudioSystem::stream_type streamType;
-};
-
-// ----------------------------------------------------------------------------
-// EQ data
-double amp;
-double w;
-double sinw;
-double cosw;
-double beta;
-double a0, a1, a2, b0, b1, b2;
-double m_fa, m_fb, m_fc, m_fd, m_fe;
-double x0;  // x[n]
-double x1;  // x[n-1]
-double x2;  // x[n-2]
-double out0;// y[n]
-double out1;// y[n-1]
-double out2;// y[n-2]
-
-static float fFilterLowshelfAttenuation = FILTER_LOWSHELF_ATTENUATION;
-static float fFilterTransitionFreq = FILTER_TRANSITION_FREQ;
-static float fFilterShelfSlope = FILTER_SHELF_SLOPE;
-static float fFilterGain = FILTER_GAIN;
-static bool  bUseFilter = false;
-
-void initializeEQ() {
-
-    amp = float(pow(10.0, fFilterLowshelfAttenuation / 40.0));
-    w = 2.0 * M_PI * (fFilterTransitionFreq / DEFAULT_TTS_RATE);
-    sinw = float(sin(w));
-    cosw = float(cos(w));
-    beta = float(sqrt(amp)/fFilterShelfSlope);
-
-    // initialize low-shelf parameters
-    b0 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) + (beta*sinw));
-    b1 = 2.0F * amp * ((amp-1.0F) - ((amp+1.0F)*cosw));
-    b2 = amp * ((amp+1.0F) - ((amp-1.0F)*cosw) - (beta*sinw));
-    a0 = (amp+1.0F) + ((amp-1.0F)*cosw) + (beta*sinw);
-    a1 = 2.0F * ((amp-1.0F) + ((amp+1.0F)*cosw));
-    a2 = -((amp+1.0F) + ((amp-1.0F)*cosw) - (beta*sinw));
-
-    m_fa = fFilterGain * b0/a0;
-    m_fb = fFilterGain * b1/a0;
-    m_fc = fFilterGain * b2/a0;
-    m_fd = a1/a0;
-    m_fe = a2/a0;
-}
-
-void initializeFilter() {
-    x0 = 0.0f;
-    x1 = 0.0f;
-    x2 = 0.0f;
-    out0 = 0.0f;
-    out1 = 0.0f;
-    out2 = 0.0f;
-}
-
-void applyFilter(int16_t* buffer, size_t sampleCount) {
-
-    for (size_t i=0 ; i<sampleCount ; i++) {
-
-        x0 = (double) buffer[i];
-
-        out0 = (m_fa*x0) + (m_fb*x1) + (m_fc*x2) + (m_fd*out1) + (m_fe*out2);
-
-        x2 = x1;
-        x1 = x0;
-
-        out2 = out1;
-        out1 = out0;
-
-        if (out0 > 32767.0f) {
-            buffer[i] = 32767;
-        } else if (out0 < -32768.0f) {
-            buffer[i] = -32768;
-        } else {
-            buffer[i] = (int16_t) out0;
-        }
-    }
-}
-
-
-// ----------------------------------------------------------------------------
-static fields_t javaTTSFields;
-
-// TODO move to synth member once we have multiple simultaneous engines running
-static Mutex engineMutex;
-
-// ----------------------------------------------------------------------------
-class SynthProxyJniStorage {
-    public :
-        jobject                   tts_ref;
-        android_tts_engine_t*     mEngine;
-        void*                     mEngineLibHandle;
-        AudioTrack*               mAudioOut;
-        int8_t                    mPlayState;
-        Mutex                     mPlayLock;
-        AudioSystem::stream_type  mStreamType;
-        uint32_t                  mSampleRate;
-        uint32_t                  mAudFormat;
-        int                       mNbChannels;
-        int8_t *                  mBuffer;
-        size_t                    mBufferSize;
-        float                     mVolume[2];
-
-        SynthProxyJniStorage() {
-            tts_ref = NULL;
-            mEngine = NULL;
-            mEngineLibHandle = NULL;
-            mAudioOut = NULL;
-            mPlayState =  SYNTHPLAYSTATE_IS_STOPPED;
-            mStreamType = DEFAULT_TTS_STREAM_TYPE;
-            mSampleRate = DEFAULT_TTS_RATE;
-            mAudFormat  = DEFAULT_TTS_FORMAT;
-            mNbChannels = DEFAULT_TTS_NB_CHANNELS;
-            mBufferSize = DEFAULT_TTS_BUFFERSIZE;
-            mBuffer = new int8_t[mBufferSize];
-            memset(mBuffer, 0, mBufferSize);
-            mVolume[AudioTrack::LEFT] = DEFAULT_VOLUME;
-            mVolume[AudioTrack::RIGHT] = DEFAULT_VOLUME;
-        }
-
-        ~SynthProxyJniStorage() {
-            //LOGV("entering ~SynthProxyJniStorage()");
-            killAudio();
-            if (mEngine) {
-                mEngine->funcs->shutdown(mEngine);
-                mEngine = NULL;
-            }
-            if (mEngineLibHandle) {
-                //LOGV("~SynthProxyJniStorage(): before close library");
-                int res = dlclose(mEngineLibHandle);
-                LOGE_IF( res != 0, "~SynthProxyJniStorage(): dlclose returned %d", res);
-            }
-            delete mBuffer;
-        }
-
-        void killAudio() {
-            if (mAudioOut) {
-                mAudioOut->stop();
-                delete mAudioOut;
-                mAudioOut = NULL;
-            }
-        }
-
-        void createAudioOut(AudioSystem::stream_type streamType, uint32_t rate,
-                AudioSystem::audio_format format, int channel) {
-            mSampleRate = rate;
-            mAudFormat  = format;
-            mNbChannels = channel;
-            mStreamType = streamType;
-
-            // retrieve system properties to ensure successful creation of the
-            // AudioTrack object for playback
-            int afSampleRate;
-            if (AudioSystem::getOutputSamplingRate(&afSampleRate, mStreamType) != NO_ERROR) {
-                afSampleRate = 44100;
-            }
-            int afFrameCount;
-            if (AudioSystem::getOutputFrameCount(&afFrameCount, mStreamType) != NO_ERROR) {
-                afFrameCount = 2048;
-            }
-            uint32_t afLatency;
-            if (AudioSystem::getOutputLatency(&afLatency, mStreamType) != NO_ERROR) {
-                afLatency = 500;
-            }
-            uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
-            if (minBufCount < 2) minBufCount = 2;
-            int minFrameCount = (afFrameCount * rate * minBufCount)/afSampleRate;
-
-            mPlayLock.lock();
-            mAudioOut = new AudioTrack(mStreamType, rate, format,
-                    (channel == 2) ? AudioSystem::CHANNEL_OUT_STEREO : AudioSystem::CHANNEL_OUT_MONO,
-                    minFrameCount > 4096 ? minFrameCount : 4096,
-                    0, 0, 0, 0); // not using an AudioTrack callback
-
-            if (mAudioOut->initCheck() != NO_ERROR) {
-              LOGE("createAudioOut(): AudioTrack error");
-              delete mAudioOut;
-              mAudioOut = NULL;
-            } else {
-              //LOGI("AudioTrack OK");
-              mAudioOut->setVolume(mVolume[AudioTrack::LEFT], mVolume[AudioTrack::RIGHT]);
-              LOGV("AudioTrack ready");
-            }
-            mPlayLock.unlock();
-        }
-};
-
-
-// ----------------------------------------------------------------------------
-void prepAudioTrack(SynthProxyJniStorage* pJniData, AudioSystem::stream_type streamType,
-        uint32_t rate, AudioSystem::audio_format format, int channel) {
-    // Don't bother creating a new audiotrack object if the current
-    // object is already initialized with the same audio parameters.
-    if ( pJniData->mAudioOut &&
-         (rate == pJniData->mSampleRate) &&
-         (format == pJniData->mAudFormat) &&
-         (channel == pJniData->mNbChannels) &&
-         (streamType == pJniData->mStreamType) ){
-        return;
-    }
-    if (pJniData->mAudioOut){
-        pJniData->killAudio();
-    }
-    pJniData->createAudioOut(streamType, rate, format, channel);
-}
-
-
-// ----------------------------------------------------------------------------
-/*
- * Callback from TTS engine.
- * Directly speaks using AudioTrack or write to file
- */
-extern "C" android_tts_callback_status_t
-__ttsSynthDoneCB(void ** pUserdata, uint32_t rate,
-               android_tts_audio_format_t format, int channel,
-               int8_t **pWav, size_t *pBufferSize,
-               android_tts_synth_status_t status)
-{
-    //LOGV("ttsSynthDoneCallback: %d bytes", bufferSize);
-    AudioSystem::audio_format  encoding;
-
-    if (*pUserdata == NULL){
-        LOGE("userdata == NULL");
-        return ANDROID_TTS_CALLBACK_HALT;
-    }
-    switch (format) {
-    case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
-        encoding = AudioSystem::PCM_8_BIT;
-        break;
-    case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
-        encoding = AudioSystem::PCM_16_BIT;
-        break;
-    default:
-        LOGE("Can't play, bad format");
-        return ANDROID_TTS_CALLBACK_HALT;
-    }
-    afterSynthData_t* pForAfter = (afterSynthData_t*) *pUserdata;
-    SynthProxyJniStorage* pJniData = (SynthProxyJniStorage*)(pForAfter->jniStorage);
-
-    if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY){
-        //LOGV("Direct speech");
-
-        if (*pWav == NULL) {
-            delete pForAfter;
-            pForAfter = NULL;
-            LOGV("Null: speech has completed");
-            return ANDROID_TTS_CALLBACK_HALT;
-        }
-
-        if (*pBufferSize > 0) {
-            prepAudioTrack(pJniData, pForAfter->streamType, rate, encoding, channel);
-            if (pJniData->mAudioOut) {
-                pJniData->mPlayLock.lock();
-                if(pJniData->mAudioOut->stopped()
-                        && (pJniData->mPlayState == SYNTHPLAYSTATE_IS_PLAYING)) {
-                    pJniData->mAudioOut->start();
-                }
-                pJniData->mPlayLock.unlock();
-                if (bUseFilter) {
-                    applyFilter((int16_t*)*pWav, *pBufferSize/2);
-                }
-                pJniData->mAudioOut->write(*pWav, *pBufferSize);
-                memset(*pWav, 0, *pBufferSize);
-                //LOGV("AudioTrack wrote: %d bytes", bufferSize);
-            } else {
-                LOGE("Can't play, null audiotrack");
-                delete pForAfter;
-                pForAfter = NULL;
-                return ANDROID_TTS_CALLBACK_HALT;
-            }
-        }
-    } else  if (pForAfter->usageMode == USAGEMODE_WRITE_TO_FILE) {
-        //LOGV("Save to file");
-        if (*pWav == NULL) {
-            delete pForAfter;
-            LOGV("Null: speech has completed");
-            return ANDROID_TTS_CALLBACK_HALT;
-        }
-        if (*pBufferSize > 0){
-            if (bUseFilter) {
-                applyFilter((int16_t*)*pWav, *pBufferSize/2);
-            }
-            fwrite(*pWav, 1, *pBufferSize, pForAfter->outputFile);
-            memset(*pWav, 0, *pBufferSize);
-        }
-    }
-    // Future update:
-    //      For sync points in the speech, call back into the SynthProxy class through the
-    //      javaTTSFields.synthProxyMethodPost methode to notify
-    //      playback has completed if the synthesis is done or if a marker has been reached.
-
-    if (status == ANDROID_TTS_SYNTH_DONE) {
-        // this struct was allocated in the original android_tts_SynthProxy_speak call,
-        // all processing matching this call is now done.
-        LOGV("Speech synthesis done.");
-        if (pForAfter->usageMode == USAGEMODE_PLAY_IMMEDIATELY) {
-            // only delete for direct playback. When writing to a file, we still have work to do
-            // in android_tts_SynthProxy_synthesizeToFile. The struct will be deleted there.
-            delete pForAfter;
-            pForAfter = NULL;
-        }
-        return ANDROID_TTS_CALLBACK_HALT;
-    }
-
-    // we don't update the wav (output) parameter as we'll let the next callback
-    // write at the same location, we've consumed the data already, but we need
-    // to update bufferSize to let the TTS engine know how much it can write the
-    // next time it calls this function.
-    *pBufferSize = pJniData->mBufferSize;
-
-    return ANDROID_TTS_CALLBACK_CONTINUE;
-}
-
-
-// ----------------------------------------------------------------------------
-static int
-android_tts_SynthProxy_setLowShelf(JNIEnv *env, jobject thiz, jboolean applyFilter,
-        jfloat filterGain, jfloat attenuationInDb, jfloat freqInHz, jfloat slope)
-{
-    int result = ANDROID_TTS_SUCCESS;
-
-    bUseFilter = applyFilter;
-    if (applyFilter) {
-        fFilterLowshelfAttenuation = attenuationInDb;
-        fFilterTransitionFreq = freqInHz;
-        fFilterShelfSlope = slope;
-        fFilterGain = filterGain;
-
-        if (fFilterShelfSlope != 0.0f) {
-            initializeEQ();
-        } else {
-            LOGE("Invalid slope, can't be null");
-            result = ANDROID_TTS_FAILURE;
-        }
-    }
-
-    return result;
-}
-
-// ----------------------------------------------------------------------------
-static int
-android_tts_SynthProxy_native_setup(JNIEnv *env, jobject thiz,
-        jobject weak_this, jstring nativeSoLib, jstring engConfig)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    bUseFilter = false;
-
-    SynthProxyJniStorage* pJniStorage = new SynthProxyJniStorage();
-
-    prepAudioTrack(pJniStorage,
-            DEFAULT_TTS_STREAM_TYPE, DEFAULT_TTS_RATE, DEFAULT_TTS_FORMAT, DEFAULT_TTS_NB_CHANNELS);
-
-    const char *nativeSoLibNativeString =  env->GetStringUTFChars(nativeSoLib, 0);
-    const char *engConfigString = env->GetStringUTFChars(engConfig, 0);
-
-    void *engine_lib_handle = dlopen(nativeSoLibNativeString,
-            RTLD_NOW | RTLD_LOCAL);
-    if (engine_lib_handle == NULL) {
-       LOGE("android_tts_SynthProxy_native_setup(): engine_lib_handle == NULL");
-    } else {
-        android_tts_engine_t * (*get_TtsEngine)() =
-            reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "android_getTtsEngine"));
-
-        // Support obsolete/legacy binary modules
-        if (get_TtsEngine == NULL) {
-            get_TtsEngine =
-                reinterpret_cast<android_tts_engine_t* (*)()>(dlsym(engine_lib_handle, "getTtsEngine"));
-        }
-
-        pJniStorage->mEngine = (*get_TtsEngine)();
-        pJniStorage->mEngineLibHandle = engine_lib_handle;
-
-        android_tts_engine_t *engine = pJniStorage->mEngine;
-        if (engine) {
-            Mutex::Autolock l(engineMutex);
-            engine->funcs->init(
-                engine,
-                __ttsSynthDoneCB,
-                engConfigString);
-        }
-
-        result = ANDROID_TTS_SUCCESS;
-    }
-
-    // we use a weak reference so the SynthProxy object can be garbage collected.
-    pJniStorage->tts_ref = env->NewGlobalRef(weak_this);
-
-    // save the JNI resources so we can use them (and free them) later
-    env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, (int)pJniStorage);
-
-    env->ReleaseStringUTFChars(nativeSoLib, nativeSoLibNativeString);
-    env->ReleaseStringUTFChars(engConfig, engConfigString);
-
-    return result;
-}
-
-
-static void
-android_tts_SynthProxy_native_finalize(JNIEnv *env, jobject thiz, jint jniData)
-{
-    //LOGV("entering android_tts_SynthProxy_finalize()");
-    if (jniData == 0) {
-        //LOGE("android_tts_SynthProxy_native_finalize(): invalid JNI data");
-        return;
-    }
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    env->DeleteGlobalRef(pSynthData->tts_ref);
-    delete pSynthData;
-
-    env->SetIntField(thiz, javaTTSFields.synthProxyFieldJniData, 0);
-}
-
-
-static void
-android_tts_SynthProxy_shutdown(JNIEnv *env, jobject thiz, jint jniData)
-{
-    //LOGV("entering android_tts_SynthProxy_shutdown()");
-
-    // do everything a call to finalize would
-    android_tts_SynthProxy_native_finalize(env, thiz, jniData);
-}
-
-
-static int
-android_tts_SynthProxy_isLanguageAvailable(JNIEnv *env, jobject thiz, jint jniData,
-        jstring language, jstring country, jstring variant)
-{
-    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_isLanguageAvailable(): invalid JNI data");
-        return result;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    const char *langNativeString = env->GetStringUTFChars(language, 0);
-    const char *countryNativeString = env->GetStringUTFChars(country, 0);
-    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
-
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->isLanguageAvailable(engine,langNativeString,
-                countryNativeString, variantNativeString);
-    }
-    env->ReleaseStringUTFChars(language, langNativeString);
-    env->ReleaseStringUTFChars(country, countryNativeString);
-    env->ReleaseStringUTFChars(variant, variantNativeString);
-    return result;
-}
-
-static int
-android_tts_SynthProxy_setConfig(JNIEnv *env, jobject thiz, jint jniData, jstring engineConfig)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_setConfig(): invalid JNI data");
-        return result;
-    }
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    const char *engineConfigNativeString = env->GetStringUTFChars(engineConfig, 0);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->setProperty(engine,ANDROID_TTS_ENGINE_PROPERTY_CONFIG,
-                engineConfigNativeString, strlen(engineConfigNativeString));
-    }
-    env->ReleaseStringUTFChars(engineConfig, engineConfigNativeString);
-
-    return result;
-}
-
-static int
-android_tts_SynthProxy_setLanguage(JNIEnv *env, jobject thiz, jint jniData,
-        jstring language, jstring country, jstring variant)
-{
-    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_setLanguage(): invalid JNI data");
-        return result;
-    }
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    const char *langNativeString = env->GetStringUTFChars(language, 0);
-    const char *countryNativeString = env->GetStringUTFChars(country, 0);
-    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->setLanguage(engine, langNativeString,
-                countryNativeString, variantNativeString);
-    }
-    env->ReleaseStringUTFChars(language, langNativeString);
-    env->ReleaseStringUTFChars(country, countryNativeString);
-    env->ReleaseStringUTFChars(variant, variantNativeString);
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_loadLanguage(JNIEnv *env, jobject thiz, jint jniData,
-        jstring language, jstring country, jstring variant)
-{
-    int result = ANDROID_TTS_LANG_NOT_SUPPORTED;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_loadLanguage(): invalid JNI data");
-        return result;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    const char *langNativeString = env->GetStringUTFChars(language, 0);
-    const char *countryNativeString = env->GetStringUTFChars(country, 0);
-    const char *variantNativeString = env->GetStringUTFChars(variant, 0);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->loadLanguage(engine, langNativeString,
-                countryNativeString, variantNativeString);
-    }
-    env->ReleaseStringUTFChars(language, langNativeString);
-    env->ReleaseStringUTFChars(country, countryNativeString);
-    env->ReleaseStringUTFChars(variant, variantNativeString);
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_setSpeechRate(JNIEnv *env, jobject thiz, jint jniData,
-        jint speechRate)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_setSpeechRate(): invalid JNI data");
-        return result;
-    }
-
-    int bufSize = 12;
-    char buffer [bufSize];
-    sprintf(buffer, "%d", speechRate);
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    //LOGI("setting speech rate to %d", speechRate);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->setProperty(engine, "rate", buffer, bufSize);
-    }
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_setPitch(JNIEnv *env, jobject thiz, jint jniData,
-        jint pitch)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_setPitch(): invalid JNI data");
-        return result;
-    }
-
-    Mutex::Autolock l(engineMutex);
-
-    int bufSize = 12;
-    char buffer [bufSize];
-    sprintf(buffer, "%d", pitch);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    //LOGI("setting pitch to %d", pitch);
-    android_tts_engine_t *engine = pSynthData->mEngine;
-
-    if (engine) {
-        result = engine->funcs->setProperty(engine, "pitch", buffer, bufSize);
-    }
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_synthesizeToFile(JNIEnv *env, jobject thiz, jint jniData,
-        jstring textJavaString, jstring filenameJavaString)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid JNI data");
-        return result;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    if (!pSynthData->mEngine) {
-        LOGE("android_tts_SynthProxy_synthesizeToFile(): invalid engine handle");
-        return result;
-    }
-
-    initializeFilter();
-
-    Mutex::Autolock l(engineMutex);
-
-    // Retrieve audio parameters before writing the file header
-    AudioSystem::audio_format encoding;
-    uint32_t rate = DEFAULT_TTS_RATE;
-    int channels = DEFAULT_TTS_NB_CHANNELS;
-    android_tts_engine_t *engine = pSynthData->mEngine;
-    android_tts_audio_format_t  format = ANDROID_TTS_AUDIO_FORMAT_DEFAULT;
-
-    engine->funcs->setAudioFormat(engine, &format, &rate, &channels);
-
-    switch (format) {
-    case ANDROID_TTS_AUDIO_FORMAT_PCM_16_BIT:
-        encoding = AudioSystem::PCM_16_BIT;
-        break;
-    case ANDROID_TTS_AUDIO_FORMAT_PCM_8_BIT:
-        encoding = AudioSystem::PCM_8_BIT;
-        break;
-    default:
-        LOGE("android_tts_SynthProxy_synthesizeToFile(): engine uses invalid format");
-        return result;
-    }
-
-    const char *filenameNativeString =
-            env->GetStringUTFChars(filenameJavaString, 0);
-    const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
-
-    afterSynthData_t* pForAfter = new (afterSynthData_t);
-    pForAfter->jniStorage = jniData;
-    pForAfter->usageMode  = USAGEMODE_WRITE_TO_FILE;
-
-    pForAfter->outputFile = fopen(filenameNativeString, "wb");
-
-    if (pForAfter->outputFile == NULL) {
-        LOGE("android_tts_SynthProxy_synthesizeToFile(): error creating output file");
-        delete pForAfter;
-        return result;
-    }
-
-    // Write 44 blank bytes for WAV header, then come back and fill them in
-    // after we've written the audio data
-    char header[44];
-    fwrite(header, 1, 44, pForAfter->outputFile);
-
-    unsigned int unique_identifier;
-
-    memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
-
-    result = engine->funcs->synthesizeText(engine, textNativeString,
-            pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
-
-    long filelen = ftell(pForAfter->outputFile);
-
-    int samples = (((int)filelen) - 44) / 2;
-    header[0] = 'R';
-    header[1] = 'I';
-    header[2] = 'F';
-    header[3] = 'F';
-    ((uint32_t *)(&header[4]))[0] = filelen - 8;
-    header[8] = 'W';
-    header[9] = 'A';
-    header[10] = 'V';
-    header[11] = 'E';
-
-    header[12] = 'f';
-    header[13] = 'm';
-    header[14] = 't';
-    header[15] = ' ';
-
-    ((uint32_t *)(&header[16]))[0] = 16;  // size of fmt
-
-    int sampleSizeInByte = (encoding == AudioSystem::PCM_16_BIT ? 2 : 1);
-
-    ((unsigned short *)(&header[20]))[0] = 1;  // format
-    ((unsigned short *)(&header[22]))[0] = channels;  // channels
-    ((uint32_t *)(&header[24]))[0] = rate;  // samplerate
-    ((uint32_t *)(&header[28]))[0] = rate * sampleSizeInByte * channels;// byterate
-    ((unsigned short *)(&header[32]))[0] = sampleSizeInByte * channels;  // block align
-    ((unsigned short *)(&header[34]))[0] = sampleSizeInByte * 8;  // bits per sample
-
-    header[36] = 'd';
-    header[37] = 'a';
-    header[38] = 't';
-    header[39] = 'a';
-
-    ((uint32_t *)(&header[40]))[0] = samples * 2;  // size of data
-
-    // Skip back to the beginning and rewrite the header
-    fseek(pForAfter->outputFile, 0, SEEK_SET);
-    fwrite(header, 1, 44, pForAfter->outputFile);
-
-    fflush(pForAfter->outputFile);
-    fclose(pForAfter->outputFile);
-
-    delete pForAfter;
-    pForAfter = NULL;
-
-    env->ReleaseStringUTFChars(textJavaString, textNativeString);
-    env->ReleaseStringUTFChars(filenameJavaString, filenameNativeString);
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_speak(JNIEnv *env, jobject thiz, jint jniData,
-        jstring textJavaString, jint javaStreamType, jfloat volume, jfloat pan)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_speak(): invalid JNI data");
-        return result;
-    }
-
-    initializeFilter();
-
-    Mutex::Autolock l(engineMutex);
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
-    {//scope for lock on mPlayLock
-        Mutex::Autolock _l(pSynthData->mPlayLock);
-
-        pSynthData->mPlayState = SYNTHPLAYSTATE_IS_PLAYING;
-
-        // clip volume and pan
-        float vol = (volume > 1.0f) ? 1.0f : (volume < 0.0f) ? 0.0f : volume;
-        float panning = (pan > 1.0f) ? 1.0f : (pan < -1.0f) ? -1.0f : pan;
-        // compute playback volume based on volume and pan, using balance rule, in order to avoid
-        // lowering volume when panning in center
-        pSynthData->mVolume[AudioTrack::LEFT] = vol;
-        pSynthData->mVolume[AudioTrack::RIGHT] = vol;
-        if (panning > 0.0f) {
-            pSynthData->mVolume[AudioTrack::LEFT] *= (1.0f - panning);
-        } else if (panning < 0.0f) {
-            pSynthData->mVolume[AudioTrack::RIGHT] *= (1.0f + panning);
-        }
-
-        // apply the volume if there is an output
-        if (NULL != pSynthData->mAudioOut) {
-            pSynthData->mAudioOut->setVolume(pSynthData->mVolume[AudioTrack::LEFT],
-                    pSynthData->mVolume[AudioTrack::RIGHT]);
-        }
-
-        //LOGV("android_tts_SynthProxy_speak() vol=%.3f pan=%.3f, mVolume=[%.1f %.1f]",
-        //        volume, pan,
-        //        pSynthData->mVolume[AudioTrack::LEFT], pSynthData->mVolume[AudioTrack::RIGHT]);
-    }
-
-    afterSynthData_t* pForAfter = new (afterSynthData_t);
-    pForAfter->jniStorage = jniData;
-    pForAfter->usageMode  = USAGEMODE_PLAY_IMMEDIATELY;
-    pForAfter->streamType = (AudioSystem::stream_type) javaStreamType;
-
-    if (pSynthData->mEngine) {
-        const char *textNativeString = env->GetStringUTFChars(textJavaString, 0);
-        memset(pSynthData->mBuffer, 0, pSynthData->mBufferSize);
-        android_tts_engine_t *engine = pSynthData->mEngine;
-
-        result = engine->funcs->synthesizeText(engine, textNativeString,
-                pSynthData->mBuffer, pSynthData->mBufferSize, (void *)pForAfter);
-        env->ReleaseStringUTFChars(textJavaString, textNativeString);
-    }
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_stop(JNIEnv *env, jobject thiz, jint jniData)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
-        return result;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
-    pSynthData->mPlayLock.lock();
-    pSynthData->mPlayState = SYNTHPLAYSTATE_IS_STOPPED;
-    if (pSynthData->mAudioOut) {
-        pSynthData->mAudioOut->stop();
-    }
-    pSynthData->mPlayLock.unlock();
-
-    android_tts_engine_t *engine = pSynthData->mEngine;
-    if (engine) {
-        result = engine->funcs->stop(engine);
-    }
-
-    return result;
-}
-
-
-static int
-android_tts_SynthProxy_stopSync(JNIEnv *env, jobject thiz, jint jniData)
-{
-    int result = ANDROID_TTS_FAILURE;
-
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_stop(): invalid JNI data");
-        return result;
-    }
-
-    // perform a regular stop
-    result = android_tts_SynthProxy_stop(env, thiz, jniData);
-    // but wait on the engine having released the engine mutex which protects
-    // the synthesizer resources.
-    engineMutex.lock();
-    engineMutex.unlock();
-
-    return result;
-}
-
-
-static jobjectArray
-android_tts_SynthProxy_getLanguage(JNIEnv *env, jobject thiz, jint jniData)
-{
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_getLanguage(): invalid JNI data");
-        return NULL;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-
-    if (pSynthData->mEngine) {
-        size_t bufSize = 100;
-        char lang[bufSize];
-        char country[bufSize];
-        char variant[bufSize];
-        memset(lang, 0, bufSize);
-        memset(country, 0, bufSize);
-        memset(variant, 0, bufSize);
-        jobjectArray retLocale = (jobjectArray)env->NewObjectArray(3,
-                env->FindClass("java/lang/String"), env->NewStringUTF(""));
-
-        android_tts_engine_t *engine = pSynthData->mEngine;
-        engine->funcs->getLanguage(engine, lang, country, variant);
-        env->SetObjectArrayElement(retLocale, 0, env->NewStringUTF(lang));
-        env->SetObjectArrayElement(retLocale, 1, env->NewStringUTF(country));
-        env->SetObjectArrayElement(retLocale, 2, env->NewStringUTF(variant));
-        return retLocale;
-    } else {
-        return NULL;
-    }
-}
-
-
-JNIEXPORT int JNICALL
-android_tts_SynthProxy_getRate(JNIEnv *env, jobject thiz, jint jniData)
-{
-    if (jniData == 0) {
-        LOGE("android_tts_SynthProxy_getRate(): invalid JNI data");
-        return 0;
-    }
-
-    SynthProxyJniStorage* pSynthData = (SynthProxyJniStorage*)jniData;
-    size_t bufSize = 100;
-
-    char buf[bufSize];
-    memset(buf, 0, bufSize);
-    // TODO check return codes
-    android_tts_engine_t *engine = pSynthData->mEngine;
-    if (engine) {
-        engine->funcs->getProperty(engine,"rate", buf, &bufSize);
-    }
-    return atoi(buf);
-}
-
-// Dalvik VM type signatures
-static JNINativeMethod gMethods[] = {
-    {   "native_stop",
-        "(I)I",
-        (void*)android_tts_SynthProxy_stop
-    },
-    {   "native_stopSync",
-        "(I)I",
-        (void*)android_tts_SynthProxy_stopSync
-    },
-    {   "native_speak",
-        "(ILjava/lang/String;IFF)I",
-        (void*)android_tts_SynthProxy_speak
-    },
-    {   "native_synthesizeToFile",
-        "(ILjava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_synthesizeToFile
-    },
-    {   "native_isLanguageAvailable",
-        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_isLanguageAvailable
-    },
-    {   "native_setConfig",
-            "(ILjava/lang/String;)I",
-            (void*)android_tts_SynthProxy_setConfig
-    },
-    {   "native_setLanguage",
-        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_setLanguage
-    },
-    {   "native_loadLanguage",
-        "(ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_loadLanguage
-    },
-    {   "native_setSpeechRate",
-        "(II)I",
-        (void*)android_tts_SynthProxy_setSpeechRate
-    },
-    {   "native_setPitch",
-        "(II)I",
-        (void*)android_tts_SynthProxy_setPitch
-    },
-    {   "native_getLanguage",
-        "(I)[Ljava/lang/String;",
-        (void*)android_tts_SynthProxy_getLanguage
-    },
-    {   "native_getRate",
-        "(I)I",
-        (void*)android_tts_SynthProxy_getRate
-    },
-    {   "native_shutdown",
-        "(I)V",
-        (void*)android_tts_SynthProxy_shutdown
-    },
-    {   "native_setup",
-        "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)I",
-        (void*)android_tts_SynthProxy_native_setup
-    },
-    {   "native_setLowShelf",
-        "(ZFFFF)I",
-        (void*)android_tts_SynthProxy_setLowShelf
-    },
-    {   "native_finalize",
-        "(I)V",
-        (void*)android_tts_SynthProxy_native_finalize
-    }
-};
-
-#define SP_JNIDATA_FIELD_NAME                "mJniData"
-#define SP_POSTSPEECHSYNTHESIZED_METHOD_NAME "postNativeSpeechSynthesizedInJava"
-
-static const char* const kClassPathName = "android/tts/SynthProxy";
-
-jint JNI_OnLoad(JavaVM* vm, void* reserved)
-{
-    JNIEnv* env = NULL;
-    jint result = -1;
-    jclass clazz;
-
-    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
-        LOGE("ERROR: GetEnv failed\n");
-        goto bail;
-    }
-    assert(env != NULL);
-
-    clazz = env->FindClass(kClassPathName);
-    if (clazz == NULL) {
-        LOGE("Can't find %s", kClassPathName);
-        goto bail;
-    }
-
-    javaTTSFields.synthProxyFieldJniData = NULL;
-    javaTTSFields.synthProxyMethodPost = NULL;
-
-    javaTTSFields.synthProxyFieldJniData = env->GetFieldID(clazz,
-            SP_JNIDATA_FIELD_NAME, "I");
-    if (javaTTSFields.synthProxyFieldJniData == NULL) {
-        LOGE("Can't find %s.%s field", kClassPathName, SP_JNIDATA_FIELD_NAME);
-        goto bail;
-    }
-
-    javaTTSFields.synthProxyMethodPost = env->GetStaticMethodID(clazz,
-            SP_POSTSPEECHSYNTHESIZED_METHOD_NAME, "(Ljava/lang/Object;II)V");
-    if (javaTTSFields.synthProxyMethodPost == NULL) {
-        LOGE("Can't find %s.%s method", kClassPathName, SP_POSTSPEECHSYNTHESIZED_METHOD_NAME);
-        goto bail;
-    }
-
-    if (jniRegisterNativeMethods(
-            env, kClassPathName, gMethods, NELEM(gMethods)) < 0)
-        goto bail;
-
-    /* success -- return valid version number */
-    result = JNI_VERSION_1_4;
-
- bail:
-    return result;
-}
diff --git a/packages/TtsService/proguard.flags b/packages/TtsService/proguard.flags
deleted file mode 100644
index e8bee6b..0000000
--- a/packages/TtsService/proguard.flags
+++ /dev/null
@@ -1,5 +0,0 @@
--keep class android.tts.SynthProxy {
-  int mJniData;
-  # keep all declarations for native methods
-  <methods>;
-}
diff --git a/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png b/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png
deleted file mode 100644
index f075e0f..0000000
--- a/packages/TtsService/res/drawable-hdpi/ic_launcher_text_to_speech.png
+++ /dev/null
Binary files differ
diff --git a/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png b/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png
deleted file mode 100644
index cbae7de..0000000
--- a/packages/TtsService/res/drawable-mdpi/ic_launcher_text_to_speech.png
+++ /dev/null
Binary files differ
diff --git a/packages/TtsService/src/android/tts/SynthProxy.java b/packages/TtsService/src/android/tts/SynthProxy.java
deleted file mode 100755
index f5f5fcf..0000000
--- a/packages/TtsService/src/android/tts/SynthProxy.java
+++ /dev/null
@@ -1,238 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * 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 android.tts;
-
-import android.media.AudioManager;
-import android.media.AudioSystem;
-import android.util.Log;
-import java.lang.ref.WeakReference;
-
-/**
- * @hide
- *
- * The SpeechSynthesis class provides a high-level api to create and play
- * synthesized speech. This class is used internally to talk to a native
- * TTS library that implements the interface defined in
- * frameworks/base/include/tts/TtsEngine.h
- *
- */
-@SuppressWarnings("unused")
-public class SynthProxy {
-
-    // Default parameters of a filter to be applied when using the Pico engine.
-    // Such a huge filter gain is justified by how much energy in the low frequencies is "wasted" at
-    // the output of the synthesis. The low shelving filter removes it, leaving room for
-    // amplification.
-    private final static float PICO_FILTER_GAIN = 5.0f; // linear gain
-    private final static float PICO_FILTER_LOWSHELF_ATTENUATION = -18.0f; // in dB
-    private final static float PICO_FILTER_TRANSITION_FREQ = 1100.0f;     // in Hz
-    private final static float PICO_FILTER_SHELF_SLOPE = 1.0f;            // Q
-
-    //
-    // External API
-    //
-
-    /**
-     * Constructor; pass the location of the native TTS .so to use.
-     */
-    public SynthProxy(String nativeSoLib, String engineConfig) {
-        boolean applyFilter = nativeSoLib.toLowerCase().contains("pico");
-        Log.v(TtsService.SERVICE_TAG, "About to load "+ nativeSoLib + ", applyFilter="+applyFilter);
-        native_setup(new WeakReference<SynthProxy>(this), nativeSoLib, engineConfig);
-        native_setLowShelf(applyFilter, PICO_FILTER_GAIN, PICO_FILTER_LOWSHELF_ATTENUATION,
-                PICO_FILTER_TRANSITION_FREQ, PICO_FILTER_SHELF_SLOPE);
-    }
-
-    /**
-     * Stops and clears the AudioTrack.
-     */
-    public int stop() {
-        return native_stop(mJniData);
-    }
-
-    /**
-     * Synchronous stop of the synthesizer. This method returns when the synth
-     * has completed the stop procedure and doesn't use any of the resources it
-     * was using while synthesizing.
-     *
-     * @return {@link android.speech.tts.TextToSpeech.SUCCESS} or
-     *         {@link android.speech.tts.TextToSpeech.ERROR}
-     */
-    public int stopSync() {
-        return native_stopSync(mJniData);
-    }
-
-    /**
-     * Synthesize speech and speak it directly using AudioTrack.
-     */
-    public int speak(String text, int streamType, float volume, float pan) {
-        Log.i(TAG, "speak() on stream "+ streamType);
-        if ((streamType > -1) && (streamType < AudioSystem.getNumStreamTypes())) {
-            return native_speak(mJniData, text, streamType, volume, pan);
-        } else {
-            Log.e("SynthProxy", "Trying to speak with invalid stream type " + streamType);
-            return native_speak(mJniData, text, AudioManager.STREAM_MUSIC, volume, pan);
-        }
-    }
-
-    /**
-     * Synthesize speech to a file. The current implementation writes a valid
-     * WAV file to the given path, assuming it is writable. Something like
-     * "/sdcard/???.wav" is recommended.
-     */
-    public int synthesizeToFile(String text, String filename) {
-        Log.i(TAG, "synthesizeToFile() to file "+ filename);
-        return native_synthesizeToFile(mJniData, text, filename);
-    }
-
-    /**
-     * Queries for language support.
-     * Return codes are defined in android.speech.tts.TextToSpeech
-     */
-    public int isLanguageAvailable(String language, String country, String variant) {
-        return native_isLanguageAvailable(mJniData, language, country, variant);
-    }
-
-    /**
-     * Updates the engine configuration.
-     */
-    public int setConfig(String engineConfig) {
-        return native_setConfig(mJniData, engineConfig);
-    }
-
-    /**
-     * Sets the language.
-     */
-    public int setLanguage(String language, String country, String variant) {
-        return native_setLanguage(mJniData, language, country, variant);
-    }
-
-    /**
-     * Loads the language: it's not set, but prepared for use later.
-     */
-    public int loadLanguage(String language, String country, String variant) {
-        return native_loadLanguage(mJniData, language, country, variant);
-    }
-
-    /**
-     * Sets the speech rate.
-     */
-    public final int setSpeechRate(int speechRate) {
-        return native_setSpeechRate(mJniData, speechRate);
-    }
-
-    /**
-     * Sets the pitch of the synthesized voice.
-     */
-    public final int setPitch(int pitch) {
-        return native_setPitch(mJniData, pitch);
-    }
-
-    /**
-     * Returns the currently set language, country and variant information.
-     */
-    public String[] getLanguage() {
-        return native_getLanguage(mJniData);
-    }
-
-    /**
-     * Gets the currently set rate.
-     */
-    public int getRate() {
-        return native_getRate(mJniData);
-    }
-
-    /**
-     * Shuts down the native synthesizer.
-     */
-    public void shutdown()  {
-        native_shutdown(mJniData);
-    }
-
-    //
-    // Internal
-    //
-
-    protected void finalize() {
-        native_finalize(mJniData);
-        mJniData = 0;
-    }
-
-    static {
-        System.loadLibrary("ttssynthproxy");
-    }
-
-    private final static String TAG = "SynthProxy";
-
-    /**
-     * Accessed by native methods
-     */
-    private int mJniData = 0;
-
-    private native final int native_setup(Object weak_this, String nativeSoLib,
-            String engineConfig);
-
-    private native final int native_setLowShelf(boolean applyFilter, float filterGain,
-            float attenuationInDb, float freqInHz, float slope);
-
-    private native final void native_finalize(int jniData);
-
-    private native final int native_stop(int jniData);
-
-    private native final int native_stopSync(int jniData);
-
-    private native final int native_speak(int jniData, String text, int streamType, float volume,
-            float pan);
-
-    private native final int native_synthesizeToFile(int jniData, String text, String filename);
-
-    private native final int  native_isLanguageAvailable(int jniData, String language,
-            String country, String variant);
-
-    private native final int native_setLanguage(int jniData, String language, String country,
-            String variant);
-
-    private native final int native_loadLanguage(int jniData, String language, String country,
-            String variant);
-
-    private native final int native_setConfig(int jniData, String engineConfig);
-
-    private native final int native_setSpeechRate(int jniData, int speechRate);
-
-    private native final int native_setPitch(int jniData, int speechRate);
-
-    private native final String[] native_getLanguage(int jniData);
-
-    private native final int native_getRate(int jniData);
-
-    private native final void native_shutdown(int jniData);
-
-
-    /**
-     * Callback from the C layer
-     */
-    @SuppressWarnings("unused")
-    private static void postNativeSpeechSynthesizedInJava(Object tts_ref,
-            int bufferPointer, int bufferSize) {
-
-        Log.i("TTS plugin debug", "bufferPointer: " + bufferPointer
-                + " bufferSize: " + bufferSize);
-
-        SynthProxy nativeTTS = (SynthProxy)((WeakReference)tts_ref).get();
-        // TODO notify TTS service of synthesis/playback completion,
-        //      method definition to be changed.
-    }
-}
diff --git a/packages/TtsService/src/android/tts/TtsService.java b/packages/TtsService/src/android/tts/TtsService.java
deleted file mode 100755
index c562327..0000000
--- a/packages/TtsService/src/android/tts/TtsService.java
+++ /dev/null
@@ -1,1503 +0,0 @@
-/*
- * Copyright (C) 2009 Google Inc.
- *
- * 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 android.tts;
-
-import android.app.Service;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ActivityInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.ResolveInfo;
-import android.database.Cursor;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnCompletionListener;
-import android.net.Uri;
-import android.os.IBinder;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.preference.PreferenceManager;
-import android.speech.tts.ITts.Stub;
-import android.speech.tts.ITtsCallback;
-import android.speech.tts.TextToSpeech;
-import android.util.Log;
-
-import java.io.File;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Locale;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.concurrent.TimeUnit;
-
-
-/**
- * @hide Synthesizes speech from text. This is implemented as a service so that
- *       other applications can call the TTS without needing to bundle the TTS
- *       in the build.
- *
- */
-public class TtsService extends Service implements OnCompletionListener {
-
-    private static class SpeechItem {
-        public static final int TEXT = 0;
-        public static final int EARCON = 1;
-        public static final int SILENCE = 2;
-        public static final int TEXT_TO_FILE = 3;
-        public String mText = "";
-        public ArrayList<String> mParams = null;
-        public int mType = TEXT;
-        public long mDuration = 0;
-        public String mFilename = null;
-        public String mCallingApp = "";
-
-        public SpeechItem(String source, String text, ArrayList<String> params, int itemType) {
-            mText = text;
-            mParams = params;
-            mType = itemType;
-            mCallingApp = source;
-        }
-
-        public SpeechItem(String source, long silenceTime, ArrayList<String> params) {
-            mDuration = silenceTime;
-            mParams = params;
-            mType = SILENCE;
-            mCallingApp = source;
-        }
-
-        public SpeechItem(String source, String text, ArrayList<String> params,
-                int itemType, String filename) {
-            mText = text;
-            mParams = params;
-            mType = itemType;
-            mFilename = filename;
-            mCallingApp = source;
-        }
-
-    }
-
-    /**
-     * Contains the information needed to access a sound resource; the name of
-     * the package that contains the resource and the resID of the resource
-     * within that package.
-     */
-    private static class SoundResource {
-        public String mSourcePackageName = null;
-        public int mResId = -1;
-        public String mFilename = null;
-
-        public SoundResource(String packageName, int id) {
-            mSourcePackageName = packageName;
-            mResId = id;
-            mFilename = null;
-        }
-
-        public SoundResource(String file) {
-            mSourcePackageName = null;
-            mResId = -1;
-            mFilename = file;
-        }
-    }
-    // If the speech queue is locked for more than 5 seconds, something has gone
-    // very wrong with processSpeechQueue.
-    private static final int SPEECHQUEUELOCK_TIMEOUT = 5000;
-    private static final int MAX_SPEECH_ITEM_CHAR_LENGTH = 4000;
-    private static final int MAX_FILENAME_LENGTH = 250;
-    private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;
-    // TODO use TextToSpeech.DEFAULT_SYNTH once it is unhidden
-    private static final String DEFAULT_SYNTH = "com.svox.pico";
-    private static final String ACTION = "android.intent.action.START_TTS_SERVICE";
-    private static final String CATEGORY = "android.intent.category.TTS";
-    private static final String PKGNAME = "android.tts";
-    protected static final String SERVICE_TAG = "TtsService";
-
-    private final RemoteCallbackList<ITtsCallback> mCallbacks
-            = new RemoteCallbackList<ITtsCallback>();
-
-    private HashMap<String, ITtsCallback> mCallbacksMap;
-
-    private Boolean mIsSpeaking;
-    private Boolean mSynthBusy;
-    private ArrayList<SpeechItem> mSpeechQueue;
-    private HashMap<String, SoundResource> mEarcons;
-    private HashMap<String, SoundResource> mUtterances;
-    private MediaPlayer mPlayer;
-    private SpeechItem mCurrentSpeechItem;
-    private HashMap<SpeechItem, Boolean> mKillList; // Used to ensure that in-flight synth calls
-                                                    // are killed when stop is used.
-    private TtsService mSelf;
-
-    private ContentResolver mResolver;
-
-    // lock for the speech queue (mSpeechQueue) and the current speech item (mCurrentSpeechItem)
-    private final ReentrantLock speechQueueLock = new ReentrantLock();
-    private final ReentrantLock synthesizerLock = new ReentrantLock();
-
-    private static SynthProxy sNativeSynth = null;
-    private String currentSpeechEngineSOFile = "";
-
-    @Override
-    public void onCreate() {
-        super.onCreate();
-        Log.v("TtsService", "TtsService.onCreate()");
-
-        mResolver = getContentResolver();
-
-        currentSpeechEngineSOFile = "";
-        setEngine(getDefaultEngine());
-
-        mSelf = this;
-        mIsSpeaking = false;
-        mSynthBusy = false;
-
-        mEarcons = new HashMap<String, SoundResource>();
-        mUtterances = new HashMap<String, SoundResource>();
-        mCallbacksMap = new HashMap<String, android.speech.tts.ITtsCallback>();
-
-        mSpeechQueue = new ArrayList<SpeechItem>();
-        mPlayer = null;
-        mCurrentSpeechItem = null;
-        mKillList = new HashMap<SpeechItem, Boolean>();
-
-        setDefaultSettings();
-    }
-
-    @Override
-    public void onDestroy() {
-        super.onDestroy();
-
-        killAllUtterances();
-
-        // Don't hog the media player
-        cleanUpPlayer();
-
-        if (sNativeSynth != null) {
-            sNativeSynth.shutdown();
-        }
-        sNativeSynth = null;
-
-        // Unregister all callbacks.
-        mCallbacks.kill();
-
-        Log.v(SERVICE_TAG, "onDestroy() completed");
-    }
-
-
-    private int setEngine(String enginePackageName) {
-        String soFilename = "";
-        if (isDefaultEnforced()) {
-            enginePackageName = getDefaultEngine();
-        }
-
-        // Make sure that the engine has been allowed by the user
-        if (!enginePackageName.equals(DEFAULT_SYNTH)) {
-            String[] enabledEngines = android.provider.Settings.Secure.getString(mResolver,
-                    android.provider.Settings.Secure.TTS_ENABLED_PLUGINS).split(" ");
-            boolean isEnabled = false;
-            for (int i=0; i<enabledEngines.length; i++) {
-                if (enabledEngines[i].equals(enginePackageName)) {
-                    isEnabled = true;
-                    break;
-                }
-            }
-            if (!isEnabled) {
-                // Do not use an engine that the user has not enabled; fall back
-                // to using the default synthesizer.
-                enginePackageName = DEFAULT_SYNTH;
-            }
-        }
-
-        // The SVOX TTS is an exception to how the TTS packaging scheme works
-        // because it is part of the system and not a 3rd party add-on; thus
-        // its binary is actually located under /system/lib/
-        if (enginePackageName.equals(DEFAULT_SYNTH)) {
-            soFilename = "/system/lib/libttspico.so";
-        } else {
-            // Find the package
-            Intent intent = new Intent("android.intent.action.START_TTS_ENGINE");
-            intent.setPackage(enginePackageName);
-            ResolveInfo[] enginesArray = new ResolveInfo[0];
-            PackageManager pm = getPackageManager();
-            List <ResolveInfo> resolveInfos = pm.queryIntentActivities(intent, 0);
-            if ((resolveInfos == null) || resolveInfos.isEmpty()) {
-                Log.e(SERVICE_TAG, "Invalid TTS Engine Package: " + enginePackageName);
-                return TextToSpeech.ERROR;
-            }
-            enginesArray = resolveInfos.toArray(enginesArray);
-            // Generate the TTS .so filename from the package
-            ActivityInfo aInfo = enginesArray[0].activityInfo;
-            soFilename = aInfo.name.replace(aInfo.packageName + ".", "") + ".so";
-            soFilename = soFilename.toLowerCase();
-            soFilename = "/data/data/" + aInfo.packageName + "/lib/libtts" + soFilename;
-        }
-
-        if (currentSpeechEngineSOFile.equals(soFilename)) {
-            return TextToSpeech.SUCCESS;
-        }
-
-        File f = new File(soFilename);
-        if (!f.exists()) {
-            Log.e(SERVICE_TAG, "Invalid TTS Binary: " + soFilename);
-            return TextToSpeech.ERROR;
-        }
-
-        if (sNativeSynth != null) {
-            sNativeSynth.stopSync();
-            sNativeSynth.shutdown();
-            sNativeSynth = null;
-        }
-
-        // Load the engineConfig from the plugin if it has any special configuration
-        // to be loaded. By convention, if an engine wants the TTS framework to pass
-        // in any configuration, it must put it into its content provider which has the URI:
-        // content://<packageName>.providers.SettingsProvider
-        // That content provider must provide a Cursor which returns the String that
-        // is to be passed back to the native .so file for the plugin when getString(0) is
-        // called on it.
-        // Note that the TTS framework does not care what this String data is: it is something
-        // that comes from the engine plugin and is consumed only by the engine plugin itself.
-        String engineConfig = "";
-        Cursor c = getContentResolver().query(Uri.parse("content://" + enginePackageName
-                + ".providers.SettingsProvider"), null, null, null, null);
-        if (c != null){
-            c.moveToFirst();
-            engineConfig = c.getString(0);
-            c.close();
-        }
-        sNativeSynth = new SynthProxy(soFilename, engineConfig);
-        currentSpeechEngineSOFile = soFilename;
-        return TextToSpeech.SUCCESS;
-    }
-
-
-
-    private void setDefaultSettings() {
-        setLanguage("", this.getDefaultLanguage(), getDefaultCountry(), getDefaultLocVariant());
-
-        // speech rate
-        setSpeechRate("", getDefaultRate());
-    }
-
-
-    private boolean isDefaultEnforced() {
-        return (android.provider.Settings.Secure.getInt(mResolver,
-                    android.provider.Settings.Secure.TTS_USE_DEFAULTS,
-                    TextToSpeech.Engine.USE_DEFAULTS)
-                == 1 );
-    }
-
-    private String getDefaultEngine() {
-        String defaultEngine = android.provider.Settings.Secure.getString(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_SYNTH);
-        if (defaultEngine == null) {
-            return TextToSpeech.Engine.DEFAULT_SYNTH;
-        } else {
-            return defaultEngine;
-        }
-    }
-
-    private int getDefaultRate() {
-        return android.provider.Settings.Secure.getInt(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_RATE,
-                TextToSpeech.Engine.DEFAULT_RATE);
-    }
-
-    private int getDefaultPitch() {
-        // Pitch is not user settable; the default pitch is always 100.
-        return 100;
-    }
-
-    private String getDefaultLanguage() {
-        String defaultLang = android.provider.Settings.Secure.getString(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_LANG);
-        if (defaultLang == null) {
-            // no setting found, use the current Locale to determine the default language
-            return Locale.getDefault().getISO3Language();
-        } else {
-            return defaultLang;
-        }
-    }
-
-
-    private String getDefaultCountry() {
-        String defaultCountry = android.provider.Settings.Secure.getString(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_COUNTRY);
-        if (defaultCountry == null) {
-            // no setting found, use the current Locale to determine the default country
-            return Locale.getDefault().getISO3Country();
-        } else {
-            return defaultCountry;
-        }
-    }
-
-
-    private String getDefaultLocVariant() {
-        String defaultVar = android.provider.Settings.Secure.getString(mResolver,
-                android.provider.Settings.Secure.TTS_DEFAULT_VARIANT);
-        if (defaultVar == null) {
-            // no setting found, use the current Locale to determine the default variant
-            return Locale.getDefault().getVariant();
-        } else {
-            return defaultVar;
-        }
-    }
-
-
-    private int setSpeechRate(String callingApp, int rate) {
-        int res = TextToSpeech.ERROR;
-        try {
-            if (isDefaultEnforced()) {
-                res = sNativeSynth.setSpeechRate(getDefaultRate());
-            } else {
-                res = sNativeSynth.setSpeechRate(rate);
-            }
-        } catch (NullPointerException e) {
-            // synth will become null during onDestroy()
-            res = TextToSpeech.ERROR;
-        }
-        return res;
-    }
-
-
-    private int setPitch(String callingApp, int pitch) {
-        int res = TextToSpeech.ERROR;
-        try {
-            res = sNativeSynth.setPitch(pitch);
-        } catch (NullPointerException e) {
-            // synth will become null during onDestroy()
-            res = TextToSpeech.ERROR;
-        }
-        return res;
-    }
-
-
-    private int isLanguageAvailable(String lang, String country, String variant) {
-        int res = TextToSpeech.LANG_NOT_SUPPORTED;
-        try {
-            res = sNativeSynth.isLanguageAvailable(lang, country, variant);
-        } catch (NullPointerException e) {
-            // synth will become null during onDestroy()
-            res = TextToSpeech.LANG_NOT_SUPPORTED;
-        }
-        return res;
-    }
-
-
-    private String[] getLanguage() {
-        try {
-            return sNativeSynth.getLanguage();
-        } catch (Exception e) {
-            return null;
-        }
-    }
-
-
-    private int setLanguage(String callingApp, String lang, String country, String variant) {
-        Log.v(SERVICE_TAG, "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
-        int res = TextToSpeech.ERROR;
-        try {
-            if (isDefaultEnforced()) {
-                res = sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
-                        getDefaultLocVariant());
-            } else {
-                res = sNativeSynth.setLanguage(lang, country, variant);
-            }
-        } catch (NullPointerException e) {
-            // synth will become null during onDestroy()
-            res = TextToSpeech.ERROR;
-        }
-        return res;
-    }
-
-
-    /**
-     * Adds a sound resource to the TTS.
-     *
-     * @param text
-     *            The text that should be associated with the sound resource
-     * @param packageName
-     *            The name of the package which has the sound resource
-     * @param resId
-     *            The resource ID of the sound within its package
-     */
-    private void addSpeech(String callingApp, String text, String packageName, int resId) {
-        mUtterances.put(text, new SoundResource(packageName, resId));
-    }
-
-    /**
-     * Adds a sound resource to the TTS.
-     *
-     * @param text
-     *            The text that should be associated with the sound resource
-     * @param filename
-     *            The filename of the sound resource. This must be a complete
-     *            path like: (/sdcard/mysounds/mysoundbite.mp3).
-     */
-    private void addSpeech(String callingApp, String text, String filename) {
-        mUtterances.put(text, new SoundResource(filename));
-    }
-
-    /**
-     * Adds a sound resource to the TTS as an earcon.
-     *
-     * @param earcon
-     *            The text that should be associated with the sound resource
-     * @param packageName
-     *            The name of the package which has the sound resource
-     * @param resId
-     *            The resource ID of the sound within its package
-     */
-    private void addEarcon(String callingApp, String earcon, String packageName, int resId) {
-        mEarcons.put(earcon, new SoundResource(packageName, resId));
-    }
-
-    /**
-     * Adds a sound resource to the TTS as an earcon.
-     *
-     * @param earcon
-     *            The text that should be associated with the sound resource
-     * @param filename
-     *            The filename of the sound resource. This must be a complete
-     *            path like: (/sdcard/mysounds/mysoundbite.mp3).
-     */
-    private void addEarcon(String callingApp, String earcon, String filename) {
-        mEarcons.put(earcon, new SoundResource(filename));
-    }
-
-    /**
-     * Speaks the given text using the specified queueing mode and parameters.
-     *
-     * @param text
-     *            The text that should be spoken
-     * @param queueMode
-     *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
-     *            TextToSpeech.TTS_QUEUE_ADD for queued
-     * @param params
-     *            An ArrayList of parameters. This is not implemented for all
-     *            engines.
-     */
-    private int speak(String callingApp, String text, int queueMode, ArrayList<String> params) {
-        // Log.v(SERVICE_TAG, "TTS service received " + text);
-        if (queueMode == TextToSpeech.QUEUE_FLUSH) {
-            stop(callingApp);
-        } else if (queueMode == 2) {
-            stopAll(callingApp);
-        }
-        mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT));
-        if (!mIsSpeaking) {
-            processSpeechQueue();
-        }
-        return TextToSpeech.SUCCESS;
-    }
-
-    /**
-     * Plays the earcon using the specified queueing mode and parameters.
-     *
-     * @param earcon
-     *            The earcon that should be played
-     * @param queueMode
-     *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances),
-     *            TextToSpeech.TTS_QUEUE_ADD for queued
-     * @param params
-     *            An ArrayList of parameters. This is not implemented for all
-     *            engines.
-     */
-    private int playEarcon(String callingApp, String earcon, int queueMode,
-            ArrayList<String> params) {
-        if (queueMode == TextToSpeech.QUEUE_FLUSH) {
-            stop(callingApp);
-        } else if (queueMode == 2) {
-            stopAll(callingApp);
-        }
-        mSpeechQueue.add(new SpeechItem(callingApp, earcon, params, SpeechItem.EARCON));
-        if (!mIsSpeaking) {
-            processSpeechQueue();
-        }
-        return TextToSpeech.SUCCESS;
-    }
-
-    /**
-     * Stops all speech output and removes any utterances still in the queue for the calling app.
-     */
-    private int stop(String callingApp) {
-        int result = TextToSpeech.ERROR;
-        boolean speechQueueAvailable = false;
-        try{
-            speechQueueAvailable =
-                    speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
-            if (speechQueueAvailable) {
-                Log.i(SERVICE_TAG, "Stopping");
-                for (int i = mSpeechQueue.size() - 1; i > -1; i--){
-                    if (mSpeechQueue.get(i).mCallingApp.equals(callingApp)){
-                        mSpeechQueue.remove(i);
-                    }
-                }
-                if ((mCurrentSpeechItem != null) &&
-                     mCurrentSpeechItem.mCallingApp.equals(callingApp)) {
-                    try {
-                        result = sNativeSynth.stop();
-                    } catch (NullPointerException e1) {
-                        // synth will become null during onDestroy()
-                        result = TextToSpeech.ERROR;
-                    }
-                    mKillList.put(mCurrentSpeechItem, true);
-                    if (mPlayer != null) {
-                        try {
-                            mPlayer.stop();
-                        } catch (IllegalStateException e) {
-                            // Do nothing, the player is already stopped.
-                        }
-                    }
-                    mIsSpeaking = false;
-                    mCurrentSpeechItem = null;
-                } else {
-                    result = TextToSpeech.SUCCESS;
-                }
-                Log.i(SERVICE_TAG, "Stopped");
-            } else {
-                Log.e(SERVICE_TAG, "TTS stop(): queue locked longer than expected");
-                result = TextToSpeech.ERROR;
-            }
-        } catch (InterruptedException e) {
-          Log.e(SERVICE_TAG, "TTS stop: tryLock interrupted");
-          e.printStackTrace();
-        } finally {
-            // This check is needed because finally will always run; even if the
-            // method returns somewhere in the try block.
-            if (speechQueueAvailable) {
-                speechQueueLock.unlock();
-            }
-            return result;
-        }
-    }
-
-
-    /**
-     * Stops all speech output, both rendered to a file and directly spoken, and removes any
-     * utterances still in the queue globally. Files that were being written are deleted.
-     */
-    @SuppressWarnings("finally")
-    private int killAllUtterances() {
-        int result = TextToSpeech.ERROR;
-        boolean speechQueueAvailable = false;
-
-        try {
-            speechQueueAvailable = speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT,
-                    TimeUnit.MILLISECONDS);
-            if (speechQueueAvailable) {
-                // remove every single entry in the speech queue
-                mSpeechQueue.clear();
-
-                // clear the current speech item
-                if (mCurrentSpeechItem != null) {
-                    result = sNativeSynth.stopSync();
-                    mKillList.put(mCurrentSpeechItem, true);
-                    mIsSpeaking = false;
-
-                    // was the engine writing to a file?
-                    if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
-                        // delete the file that was being written
-                        if (mCurrentSpeechItem.mFilename != null) {
-                            File tempFile = new File(mCurrentSpeechItem.mFilename);
-                            Log.v(SERVICE_TAG, "Leaving behind " + mCurrentSpeechItem.mFilename);
-                            if (tempFile.exists()) {
-                                Log.v(SERVICE_TAG, "About to delete "
-                                        + mCurrentSpeechItem.mFilename);
-                                if (tempFile.delete()) {
-                                    Log.v(SERVICE_TAG, "file successfully deleted");
-                                }
-                            }
-                        }
-                    }
-
-                    mCurrentSpeechItem = null;
-                }
-            } else {
-                Log.e(SERVICE_TAG, "TTS killAllUtterances(): queue locked longer than expected");
-                result = TextToSpeech.ERROR;
-            }
-        } catch (InterruptedException e) {
-            Log.e(SERVICE_TAG, "TTS killAllUtterances(): tryLock interrupted");
-            result = TextToSpeech.ERROR;
-        } finally {
-            // This check is needed because finally will always run, even if the
-            // method returns somewhere in the try block.
-            if (speechQueueAvailable) {
-                speechQueueLock.unlock();
-            }
-            return result;
-        }
-    }
-
-
-    /**
-     * Stops all speech output and removes any utterances still in the queue globally, except
-     * those intended to be synthesized to file.
-     */
-    private int stopAll(String callingApp) {
-        int result = TextToSpeech.ERROR;
-        boolean speechQueueAvailable = false;
-        try{
-            speechQueueAvailable =
-                    speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
-            if (speechQueueAvailable) {
-                for (int i = mSpeechQueue.size() - 1; i > -1; i--){
-                    if (mSpeechQueue.get(i).mType != SpeechItem.TEXT_TO_FILE){
-                        mSpeechQueue.remove(i);
-                    }
-                }
-                if ((mCurrentSpeechItem != null) &&
-                    ((mCurrentSpeechItem.mType != SpeechItem.TEXT_TO_FILE) ||
-                      mCurrentSpeechItem.mCallingApp.equals(callingApp))) {
-                    try {
-                        result = sNativeSynth.stop();
-                    } catch (NullPointerException e1) {
-                        // synth will become null during onDestroy()
-                        result = TextToSpeech.ERROR;
-                    }
-                    mKillList.put(mCurrentSpeechItem, true);
-                    if (mPlayer != null) {
-                        try {
-                            mPlayer.stop();
-                        } catch (IllegalStateException e) {
-                            // Do nothing, the player is already stopped.
-                        }
-                    }
-                    mIsSpeaking = false;
-                    mCurrentSpeechItem = null;
-                } else {
-                    result = TextToSpeech.SUCCESS;
-                }
-                Log.i(SERVICE_TAG, "Stopped all");
-            } else {
-                Log.e(SERVICE_TAG, "TTS stopAll(): queue locked longer than expected");
-                result = TextToSpeech.ERROR;
-            }
-        } catch (InterruptedException e) {
-          Log.e(SERVICE_TAG, "TTS stopAll: tryLock interrupted");
-          e.printStackTrace();
-        } finally {
-            // This check is needed because finally will always run; even if the
-            // method returns somewhere in the try block.
-            if (speechQueueAvailable) {
-                speechQueueLock.unlock();
-            }
-            return result;
-        }
-    }
-
-    public void onCompletion(MediaPlayer arg0) {
-        // mCurrentSpeechItem may become null if it is stopped at the same
-        // time it completes.
-        SpeechItem currentSpeechItemCopy = mCurrentSpeechItem;
-        if (currentSpeechItemCopy != null) {
-            String callingApp = currentSpeechItemCopy.mCallingApp;
-            ArrayList<String> params = currentSpeechItemCopy.mParams;
-            String utteranceId = "";
-            if (params != null) {
-                for (int i = 0; i < params.size() - 1; i = i + 2) {
-                    String param = params.get(i);
-                    if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)) {
-                        utteranceId = params.get(i + 1);
-                    }
-                }
-            }
-            if (utteranceId.length() > 0) {
-                dispatchUtteranceCompletedCallback(utteranceId, callingApp);
-            }
-        }
-        processSpeechQueue();
-    }
-
-    private int playSilence(String callingApp, long duration, int queueMode,
-            ArrayList<String> params) {
-        if (queueMode == TextToSpeech.QUEUE_FLUSH) {
-            stop(callingApp);
-        }
-        mSpeechQueue.add(new SpeechItem(callingApp, duration, params));
-        if (!mIsSpeaking) {
-            processSpeechQueue();
-        }
-        return TextToSpeech.SUCCESS;
-    }
-
-    private void silence(final SpeechItem speechItem) {
-        class SilenceThread implements Runnable {
-            public void run() {
-                String utteranceId = "";
-                if (speechItem.mParams != null){
-                    for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
-                        String param = speechItem.mParams.get(i);
-                        if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
-                            utteranceId = speechItem.mParams.get(i+1);
-                        }
-                    }
-                }
-                try {
-                    Thread.sleep(speechItem.mDuration);
-                } catch (InterruptedException e) {
-                    e.printStackTrace();
-                } finally {
-                    if (utteranceId.length() > 0){
-                        dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
-                    }
-                    processSpeechQueue();
-                }
-            }
-        }
-        Thread slnc = (new Thread(new SilenceThread()));
-        slnc.setPriority(Thread.MIN_PRIORITY);
-        slnc.start();
-    }
-
-    private void speakInternalOnly(final SpeechItem speechItem) {
-        class SynthThread implements Runnable {
-            public void run() {
-                boolean synthAvailable = false;
-                String utteranceId = "";
-                try {
-                    synthAvailable = synthesizerLock.tryLock();
-                    if (!synthAvailable) {
-                        mSynthBusy = true;
-                        Thread.sleep(100);
-                        Thread synth = (new Thread(new SynthThread()));
-                        synth.start();
-                        mSynthBusy = false;
-                        return;
-                    }
-                    int streamType = DEFAULT_STREAM_TYPE;
-                    String language = "";
-                    String country = "";
-                    String variant = "";
-                    String speechRate = "";
-                    String engine = "";
-                    String pitch = "";
-                    float volume = TextToSpeech.Engine.DEFAULT_VOLUME;
-                    float pan = TextToSpeech.Engine.DEFAULT_PAN;
-                    if (speechItem.mParams != null){
-                        for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
-                            String param = speechItem.mParams.get(i);
-                            if (param != null) {
-                                if (param.equals(TextToSpeech.Engine.KEY_PARAM_RATE)) {
-                                    speechRate = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_LANGUAGE)){
-                                    language = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_COUNTRY)){
-                                    country = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VARIANT)){
-                                    variant = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
-                                    utteranceId = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_STREAM)) {
-                                    try {
-                                        streamType
-                                                = Integer.parseInt(speechItem.mParams.get(i + 1));
-                                    } catch (NumberFormatException e) {
-                                        streamType = DEFAULT_STREAM_TYPE;
-                                    }
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
-                                    engine = speechItem.mParams.get(i + 1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PITCH)) {
-                                    pitch = speechItem.mParams.get(i + 1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VOLUME)) {
-                                    try {
-                                        volume = Float.parseFloat(speechItem.mParams.get(i + 1));
-                                    } catch (NumberFormatException e) {
-                                        volume = TextToSpeech.Engine.DEFAULT_VOLUME;
-                                    }
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PAN)) {
-                                    try {
-                                        pan = Float.parseFloat(speechItem.mParams.get(i + 1));
-                                    } catch (NumberFormatException e) {
-                                        pan = TextToSpeech.Engine.DEFAULT_PAN;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    // Only do the synthesis if it has not been killed by a subsequent utterance.
-                    if (mKillList.get(speechItem) == null) {
-                        if (engine.length() > 0) {
-                            setEngine(engine);
-                        } else {
-                            setEngine(getDefaultEngine());
-                        }
-                        if (language.length() > 0){
-                            setLanguage("", language, country, variant);
-                        } else {
-                            setLanguage("", getDefaultLanguage(), getDefaultCountry(),
-                                    getDefaultLocVariant());
-                        }
-                        if (speechRate.length() > 0){
-                            setSpeechRate("", Integer.parseInt(speechRate));
-                        } else {
-                            setSpeechRate("", getDefaultRate());
-                        }
-                        if (pitch.length() > 0){
-                            setPitch("", Integer.parseInt(pitch));
-                        } else {
-                            setPitch("", getDefaultPitch());
-                        }
-                        try {
-                            sNativeSynth.speak(speechItem.mText, streamType, volume, pan);
-                        } catch (NullPointerException e) {
-                            // synth will become null during onDestroy()
-                            Log.v(SERVICE_TAG, " null synth, can't speak");
-                        }
-                    }
-                } catch (InterruptedException e) {
-                    Log.e(SERVICE_TAG, "TTS speakInternalOnly(): tryLock interrupted");
-                    e.printStackTrace();
-                } finally {
-                    // This check is needed because finally will always run;
-                    // even if the
-                    // method returns somewhere in the try block.
-                    if (utteranceId.length() > 0){
-                        dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
-                    }
-                    if (synthAvailable) {
-                        synthesizerLock.unlock();
-                        processSpeechQueue();
-                    }
-                }
-            }
-        }
-        Thread synth = (new Thread(new SynthThread()));
-        synth.setPriority(Thread.MAX_PRIORITY);
-        synth.start();
-    }
-
-    private void synthToFileInternalOnly(final SpeechItem speechItem) {
-        class SynthThread implements Runnable {
-            public void run() {
-                boolean synthAvailable = false;
-                String utteranceId = "";
-                Log.i(SERVICE_TAG, "Synthesizing to " + speechItem.mFilename);
-                try {
-                    synthAvailable = synthesizerLock.tryLock();
-                    if (!synthAvailable) {
-                        synchronized (this) {
-                            mSynthBusy = true;
-                        }
-                        Thread.sleep(100);
-                        Thread synth = (new Thread(new SynthThread()));
-                        synth.start();
-                        synchronized (this) {
-                            mSynthBusy = false;
-                        }
-                        return;
-                    }
-                    String language = "";
-                    String country = "";
-                    String variant = "";
-                    String speechRate = "";
-                    String engine = "";
-                    String pitch = "";
-                    if (speechItem.mParams != null){
-                        for (int i = 0; i < speechItem.mParams.size() - 1; i = i + 2){
-                            String param = speechItem.mParams.get(i);
-                            if (param != null) {
-                                if (param.equals(TextToSpeech.Engine.KEY_PARAM_RATE)) {
-                                    speechRate = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_LANGUAGE)){
-                                    language = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_COUNTRY)){
-                                    country = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_VARIANT)){
-                                    variant = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID)){
-                                    utteranceId = speechItem.mParams.get(i+1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
-                                    engine = speechItem.mParams.get(i + 1);
-                                } else if (param.equals(TextToSpeech.Engine.KEY_PARAM_PITCH)) {
-                                    pitch = speechItem.mParams.get(i + 1);
-                                }
-                            }
-                        }
-                    }
-                    // Only do the synthesis if it has not been killed by a subsequent utterance.
-                    if (mKillList.get(speechItem) == null){
-                        if (engine.length() > 0) {
-                            setEngine(engine);
-                        } else {
-                            setEngine(getDefaultEngine());
-                        }
-                        if (language.length() > 0){
-                            setLanguage("", language, country, variant);
-                        } else {
-                            setLanguage("", getDefaultLanguage(), getDefaultCountry(),
-                                    getDefaultLocVariant());
-                        }
-                        if (speechRate.length() > 0){
-                            setSpeechRate("", Integer.parseInt(speechRate));
-                        } else {
-                            setSpeechRate("", getDefaultRate());
-                        }
-                        if (pitch.length() > 0){
-                            setPitch("", Integer.parseInt(pitch));
-                        } else {
-                            setPitch("", getDefaultPitch());
-                        }
-                        try {
-                            sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
-                        } catch (NullPointerException e) {
-                            // synth will become null during onDestroy()
-                            Log.v(SERVICE_TAG, " null synth, can't synthesize to file");
-                        }
-                    }
-                } catch (InterruptedException e) {
-                    Log.e(SERVICE_TAG, "TTS synthToFileInternalOnly(): tryLock interrupted");
-                    e.printStackTrace();
-                } finally {
-                    // This check is needed because finally will always run;
-                    // even if the
-                    // method returns somewhere in the try block.
-                    if (utteranceId.length() > 0){
-                        dispatchUtteranceCompletedCallback(utteranceId, speechItem.mCallingApp);
-                    }
-                    if (synthAvailable) {
-                        synthesizerLock.unlock();
-                        processSpeechQueue();
-                    }
-                }
-            }
-        }
-        Thread synth = (new Thread(new SynthThread()));
-        synth.setPriority(Thread.MAX_PRIORITY);
-        synth.start();
-    }
-
-    private SoundResource getSoundResource(SpeechItem speechItem) {
-        SoundResource sr = null;
-        String text = speechItem.mText;
-        if (speechItem.mType == SpeechItem.SILENCE) {
-            // Do nothing if this is just silence
-        } else if (speechItem.mType == SpeechItem.EARCON) {
-            sr = mEarcons.get(text);
-        } else {
-            sr = mUtterances.get(text);
-        }
-        return sr;
-    }
-
-    private void broadcastTtsQueueProcessingCompleted(){
-        Intent i = new Intent(TextToSpeech.ACTION_TTS_QUEUE_PROCESSING_COMPLETED);
-        sendBroadcast(i);
-    }
-
-
-    private void dispatchUtteranceCompletedCallback(String utteranceId, String packageName) {
-        ITtsCallback cb = mCallbacksMap.get(packageName);
-        if (cb == null){
-            return;
-        }
-        Log.v(SERVICE_TAG, "TTS callback: dispatch started");
-        // Broadcast to all clients the new value.
-        final int N = mCallbacks.beginBroadcast();
-        try {
-            cb.utteranceCompleted(utteranceId);
-        } catch (RemoteException e) {
-            // The RemoteCallbackList will take care of removing
-            // the dead object for us.
-        }
-        mCallbacks.finishBroadcast();
-        Log.v(SERVICE_TAG, "TTS callback: dispatch completed to " + N);
-    }
-
-    private SpeechItem splitCurrentTextIfNeeded(SpeechItem currentSpeechItem){
-        if (currentSpeechItem.mText.length() < MAX_SPEECH_ITEM_CHAR_LENGTH){
-            return currentSpeechItem;
-        } else {
-            String callingApp = currentSpeechItem.mCallingApp;
-            ArrayList<SpeechItem> splitItems = new ArrayList<SpeechItem>();
-            int start = 0;
-            int end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
-            String splitText;
-            SpeechItem splitItem;
-            while (end < currentSpeechItem.mText.length()){
-                splitText = currentSpeechItem.mText.substring(start, end);
-                splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
-                splitItems.add(splitItem);
-                start = end;
-                end = start + MAX_SPEECH_ITEM_CHAR_LENGTH - 1;
-            }
-            splitText = currentSpeechItem.mText.substring(start);
-            splitItem = new SpeechItem(callingApp, splitText, null, SpeechItem.TEXT);
-            splitItems.add(splitItem);
-            mSpeechQueue.remove(0);
-            for (int i = splitItems.size() - 1; i >= 0; i--){
-                mSpeechQueue.add(0, splitItems.get(i));
-            }
-            return mSpeechQueue.get(0);
-        }
-    }
-
-    private void processSpeechQueue() {
-        boolean speechQueueAvailable = false;
-        synchronized (this) {
-            if (mSynthBusy){
-                // There is already a synth thread waiting to run.
-                return;
-            }
-        }
-        try {
-            speechQueueAvailable =
-                    speechQueueLock.tryLock(SPEECHQUEUELOCK_TIMEOUT, TimeUnit.MILLISECONDS);
-            if (!speechQueueAvailable) {
-                Log.e(SERVICE_TAG, "processSpeechQueue - Speech queue is unavailable.");
-                return;
-            }
-            if (mSpeechQueue.size() < 1) {
-                mIsSpeaking = false;
-                mKillList.clear();
-                broadcastTtsQueueProcessingCompleted();
-                return;
-            }
-
-            mCurrentSpeechItem = mSpeechQueue.get(0);
-            mIsSpeaking = true;
-            SoundResource sr = getSoundResource(mCurrentSpeechItem);
-            // Synth speech as needed - synthesizer should call
-            // processSpeechQueue to continue running the queue
-            // Log.v(SERVICE_TAG, "TTS processing: " + mCurrentSpeechItem.mText);
-            if (sr == null) {
-                if (mCurrentSpeechItem.mType == SpeechItem.TEXT) {
-                    mCurrentSpeechItem = splitCurrentTextIfNeeded(mCurrentSpeechItem);
-                    speakInternalOnly(mCurrentSpeechItem);
-                } else if (mCurrentSpeechItem.mType == SpeechItem.TEXT_TO_FILE) {
-                    synthToFileInternalOnly(mCurrentSpeechItem);
-                } else {
-                    // This is either silence or an earcon that was missing
-                    silence(mCurrentSpeechItem);
-                }
-            } else {
-                cleanUpPlayer();
-                if (sr.mSourcePackageName == PKGNAME) {
-                    // Utterance is part of the TTS library
-                    mPlayer = MediaPlayer.create(this, sr.mResId);
-                } else if (sr.mSourcePackageName != null) {
-                    // Utterance is part of the app calling the library
-                    Context ctx;
-                    try {
-                        ctx = this.createPackageContext(sr.mSourcePackageName, 0);
-                    } catch (NameNotFoundException e) {
-                        e.printStackTrace();
-                        mSpeechQueue.remove(0); // Remove it from the queue and
-                        // move on
-                        mIsSpeaking = false;
-                        return;
-                    }
-                    mPlayer = MediaPlayer.create(ctx, sr.mResId);
-                } else {
-                    // Utterance is coming from a file
-                    mPlayer = MediaPlayer.create(this, Uri.parse(sr.mFilename));
-                }
-
-                // Check if Media Server is dead; if it is, clear the queue and
-                // give up for now - hopefully, it will recover itself.
-                if (mPlayer == null) {
-                    mSpeechQueue.clear();
-                    mIsSpeaking = false;
-                    return;
-                }
-                mPlayer.setOnCompletionListener(this);
-                try {
-                    mPlayer.setAudioStreamType(getStreamTypeFromParams(mCurrentSpeechItem.mParams));
-                    mPlayer.start();
-                } catch (IllegalStateException e) {
-                    mSpeechQueue.clear();
-                    mIsSpeaking = false;
-                    cleanUpPlayer();
-                    return;
-                }
-            }
-            if (mSpeechQueue.size() > 0) {
-                mSpeechQueue.remove(0);
-            }
-        } catch (InterruptedException e) {
-          Log.e(SERVICE_TAG, "TTS processSpeechQueue: tryLock interrupted");
-          e.printStackTrace();
-        } finally {
-            // This check is needed because finally will always run; even if the
-            // method returns somewhere in the try block.
-            if (speechQueueAvailable) {
-                speechQueueLock.unlock();
-            }
-        }
-    }
-
-    private int getStreamTypeFromParams(ArrayList<String> paramList) {
-        int streamType = DEFAULT_STREAM_TYPE;
-        if (paramList == null) {
-            return streamType;
-        }
-        for (int i = 0; i < paramList.size() - 1; i = i + 2) {
-            String param = paramList.get(i);
-            if ((param != null) && (param.equals(TextToSpeech.Engine.KEY_PARAM_STREAM))) {
-                try {
-                    streamType = Integer.parseInt(paramList.get(i + 1));
-                } catch (NumberFormatException e) {
-                    streamType = DEFAULT_STREAM_TYPE;
-                }
-            }
-        }
-        return streamType;
-    }
-
-    private void cleanUpPlayer() {
-        if (mPlayer != null) {
-            mPlayer.release();
-            mPlayer = null;
-        }
-    }
-
-    /**
-     * Synthesizes the given text to a file using the specified parameters.
-     *
-     * @param text
-     *            The String of text that should be synthesized
-     * @param params
-     *            An ArrayList of parameters. The first element of this array
-     *            controls the type of voice to use.
-     * @param filename
-     *            The string that gives the full output filename; it should be
-     *            something like "/sdcard/myappsounds/mysound.wav".
-     * @return A boolean that indicates if the synthesis can be started
-     */
-    private boolean synthesizeToFile(String callingApp, String text, ArrayList<String> params,
-            String filename) {
-        // Don't allow a filename that is too long
-        if (filename.length() > MAX_FILENAME_LENGTH) {
-            return false;
-        }
-        // Don't allow anything longer than the max text length; since this
-        // is synthing to a file, don't even bother splitting it.
-        if (text.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
-            return false;
-        }
-        // Check that the output file can be created
-        try {
-            File tempFile = new File(filename);
-            if (tempFile.exists()) {
-                Log.v("TtsService", "File " + filename + " exists, deleting.");
-                tempFile.delete();
-            }
-            if (!tempFile.createNewFile()) {
-                Log.e("TtsService", "Unable to synthesize to file: can't create " + filename);
-                return false;
-            }
-            tempFile.delete();
-        } catch (IOException e) {
-            Log.e("TtsService", "Can't create " + filename + " due to exception " + e);
-            return false;
-        }
-        mSpeechQueue.add(new SpeechItem(callingApp, text, params, SpeechItem.TEXT_TO_FILE, filename));
-        if (!mIsSpeaking) {
-            processSpeechQueue();
-        }
-        return true;
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        if (ACTION.equals(intent.getAction())) {
-            for (String category : intent.getCategories()) {
-                if (category.equals(CATEGORY)) {
-                    return mBinder;
-                }
-            }
-        }
-        return null;
-    }
-
-    private final android.speech.tts.ITts.Stub mBinder = new Stub() {
-
-        public int registerCallback(String packageName, ITtsCallback cb) {
-            if (cb != null) {
-                mCallbacks.register(cb);
-                mCallbacksMap.put(packageName, cb);
-                return TextToSpeech.SUCCESS;
-            }
-            return TextToSpeech.ERROR;
-        }
-
-        public int unregisterCallback(String packageName, ITtsCallback cb) {
-            if (cb != null) {
-                mCallbacksMap.remove(packageName);
-                mCallbacks.unregister(cb);
-                return TextToSpeech.SUCCESS;
-            }
-            return TextToSpeech.ERROR;
-        }
-
-        /**
-         * Speaks the given text using the specified queueing mode and
-         * parameters.
-         *
-         * @param text
-         *            The text that should be spoken
-         * @param queueMode
-         *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
-         *            TextToSpeech.TTS_QUEUE_ADD for queued
-         * @param params
-         *            An ArrayList of parameters. The first element of this
-         *            array controls the type of voice to use.
-         */
-        public int speak(String callingApp, String text, int queueMode, String[] params) {
-            ArrayList<String> speakingParams = new ArrayList<String>();
-            if (params != null) {
-                speakingParams = new ArrayList<String>(Arrays.asList(params));
-            }
-            return mSelf.speak(callingApp, text, queueMode, speakingParams);
-        }
-
-        /**
-         * Plays the earcon using the specified queueing mode and parameters.
-         *
-         * @param earcon
-         *            The earcon that should be played
-         * @param queueMode
-         *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
-         *            TextToSpeech.TTS_QUEUE_ADD for queued
-         * @param params
-         *            An ArrayList of parameters.
-         */
-        public int playEarcon(String callingApp, String earcon, int queueMode, String[] params) {
-            ArrayList<String> speakingParams = new ArrayList<String>();
-            if (params != null) {
-                speakingParams = new ArrayList<String>(Arrays.asList(params));
-            }
-            return mSelf.playEarcon(callingApp, earcon, queueMode, speakingParams);
-        }
-
-        /**
-         * Plays the silence using the specified queueing mode and parameters.
-         *
-         * @param duration
-         *            The duration of the silence that should be played
-         * @param queueMode
-         *            TextToSpeech.TTS_QUEUE_FLUSH for no queue (interrupts all previous utterances)
-         *            TextToSpeech.TTS_QUEUE_ADD for queued
-         * @param params
-         *            An ArrayList of parameters.
-         */
-        public int playSilence(String callingApp, long duration, int queueMode, String[] params) {
-            ArrayList<String> speakingParams = new ArrayList<String>();
-            if (params != null) {
-                speakingParams = new ArrayList<String>(Arrays.asList(params));
-            }
-            return mSelf.playSilence(callingApp, duration, queueMode, speakingParams);
-        }
-
-        /**
-         * Stops all speech output and removes any utterances still in the
-         * queue.
-         */
-        public int stop(String callingApp) {
-            return mSelf.stop(callingApp);
-        }
-
-        /**
-         * Returns whether or not the TTS is speaking.
-         *
-         * @return Boolean to indicate whether or not the TTS is speaking
-         */
-        public boolean isSpeaking() {
-            return (mSelf.mIsSpeaking && (mSpeechQueue.size() < 1));
-        }
-
-        /**
-         * Adds a sound resource to the TTS.
-         *
-         * @param text
-         *            The text that should be associated with the sound resource
-         * @param packageName
-         *            The name of the package which has the sound resource
-         * @param resId
-         *            The resource ID of the sound within its package
-         */
-        public void addSpeech(String callingApp, String text, String packageName, int resId) {
-            mSelf.addSpeech(callingApp, text, packageName, resId);
-        }
-
-        /**
-         * Adds a sound resource to the TTS.
-         *
-         * @param text
-         *            The text that should be associated with the sound resource
-         * @param filename
-         *            The filename of the sound resource. This must be a
-         *            complete path like: (/sdcard/mysounds/mysoundbite.mp3).
-         */
-        public void addSpeechFile(String callingApp, String text, String filename) {
-            mSelf.addSpeech(callingApp, text, filename);
-        }
-
-        /**
-         * Adds a sound resource to the TTS as an earcon.
-         *
-         * @param earcon
-         *            The text that should be associated with the sound resource
-         * @param packageName
-         *            The name of the package which has the sound resource
-         * @param resId
-         *            The resource ID of the sound within its package
-         */
-        public void addEarcon(String callingApp, String earcon, String packageName, int resId) {
-            mSelf.addEarcon(callingApp, earcon, packageName, resId);
-        }
-
-        /**
-         * Adds a sound resource to the TTS as an earcon.
-         *
-         * @param earcon
-         *            The text that should be associated with the sound resource
-         * @param filename
-         *            The filename of the sound resource. This must be a
-         *            complete path like: (/sdcard/mysounds/mysoundbite.mp3).
-         */
-        public void addEarconFile(String callingApp, String earcon, String filename) {
-            mSelf.addEarcon(callingApp, earcon, filename);
-        }
-
-        /**
-         * Sets the speech rate for the TTS. Note that this will only have an
-         * effect on synthesized speech; it will not affect pre-recorded speech.
-         *
-         * @param speechRate
-         *            The speech rate that should be used
-         */
-        public int setSpeechRate(String callingApp, int speechRate) {
-            return mSelf.setSpeechRate(callingApp, speechRate);
-        }
-
-        /**
-         * Sets the pitch for the TTS. Note that this will only have an
-         * effect on synthesized speech; it will not affect pre-recorded speech.
-         *
-         * @param pitch
-         *            The pitch that should be used for the synthesized voice
-         */
-        public int setPitch(String callingApp, int pitch) {
-            return mSelf.setPitch(callingApp, pitch);
-        }
-
-        /**
-         * Returns the level of support for the specified language.
-         *
-         * @param lang  the three letter ISO language code.
-         * @param country  the three letter ISO country code.
-         * @param variant  the variant code associated with the country and language pair.
-         * @return one of TTS_LANG_NOT_SUPPORTED, TTS_LANG_MISSING_DATA, TTS_LANG_AVAILABLE,
-         *      TTS_LANG_COUNTRY_AVAILABLE, TTS_LANG_COUNTRY_VAR_AVAILABLE as defined in
-         *      android.speech.tts.TextToSpeech.
-         */
-        public int isLanguageAvailable(String lang, String country, String variant,
-                String[] params) {
-            for (int i = 0; i < params.length - 1; i = i + 2){
-                String param = params[i];
-                if (param != null) {
-                    if (param.equals(TextToSpeech.Engine.KEY_PARAM_ENGINE)) {
-                        mSelf.setEngine(params[i + 1]);
-                        break;
-                    }
-                }
-            }
-            return mSelf.isLanguageAvailable(lang, country, variant);
-        }
-
-        /**
-         * Returns the currently set language / country / variant strings representing the
-         * language used by the TTS engine.
-         * @return null is no language is set, or an array of 3 string containing respectively
-         *      the language, country and variant.
-         */
-        public String[] getLanguage() {
-            return mSelf.getLanguage();
-        }
-
-        /**
-         * Sets the speech rate for the TTS, which affects the synthesized voice.
-         *
-         * @param lang  the three letter ISO language code.
-         * @param country  the three letter ISO country code.
-         * @param variant  the variant code associated with the country and language pair.
-         */
-        public int setLanguage(String callingApp, String lang, String country, String variant) {
-            return mSelf.setLanguage(callingApp, lang, country, variant);
-        }
-
-        /**
-         * Synthesizes the given text to a file using the specified
-         * parameters.
-         *
-         * @param text
-         *            The String of text that should be synthesized
-         * @param params
-         *            An ArrayList of parameters. The first element of this
-         *            array controls the type of voice to use.
-         * @param filename
-         *            The string that gives the full output filename; it should
-         *            be something like "/sdcard/myappsounds/mysound.wav".
-         * @return A boolean that indicates if the synthesis succeeded
-         */
-        public boolean synthesizeToFile(String callingApp, String text, String[] params,
-                String filename) {
-            ArrayList<String> speakingParams = new ArrayList<String>();
-            if (params != null) {
-                speakingParams = new ArrayList<String>(Arrays.asList(params));
-            }
-            return mSelf.synthesizeToFile(callingApp, text, speakingParams, filename);
-        }
-
-        /**
-         * Sets the speech synthesis engine for the TTS by specifying its packagename
-         *
-         * @param packageName  the packageName of the speech synthesis engine (ie, "com.svox.pico")
-         *
-         * @return SUCCESS or ERROR as defined in android.speech.tts.TextToSpeech.
-         */
-        public int setEngineByPackageName(String packageName) {
-            return mSelf.setEngine(packageName);
-        }
-
-        /**
-         * Returns the packagename of the default speech synthesis engine.
-         *
-         * @return Packagename of the TTS engine that the user has chosen as their default.
-         */
-        public String getDefaultEngine() {
-            return mSelf.getDefaultEngine();
-        }
-
-        /**
-         * Returns whether or not the user is forcing their defaults to override the
-         * Text-To-Speech settings set by applications.
-         *
-         * @return Whether or not defaults are enforced.
-         */
-        public boolean areDefaultsEnforced() {
-            return mSelf.isDefaultEnforced();
-        }
-
-    };
-
-}