summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java72
1 files changed, 56 insertions, 16 deletions
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index a08ba2a441ae..48739ba0f3a8 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -260,6 +260,17 @@ public abstract class TextToSpeechService extends Service {
return old;
}
+ private synchronized SpeechItem maybeRemoveCurrentSpeechItem(String callingApp) {
+ if (mCurrentSpeechItem != null &&
+ TextUtils.equals(mCurrentSpeechItem.getCallingApp(), callingApp)) {
+ SpeechItem current = mCurrentSpeechItem;
+ mCurrentSpeechItem = null;
+ return current;
+ }
+
+ return null;
+ }
+
public boolean isSpeaking() {
return getCurrentSpeechItem() != null;
}
@@ -287,14 +298,9 @@ public abstract class TextToSpeechService extends Service {
}
if (queueMode == TextToSpeech.QUEUE_FLUSH) {
- stop(speechItem.getCallingApp());
+ stopForApp(speechItem.getCallingApp());
} else if (queueMode == TextToSpeech.QUEUE_DESTROY) {
- // Stop the current speech item.
- stop(speechItem.getCallingApp());
- // Remove all other items from the queue.
- removeCallbacksAndMessages(null);
- // Remove all pending playback as well.
- mAudioPlaybackHandler.removeAllItems();
+ stopAll();
}
Runnable runnable = new Runnable() {
@Override
@@ -305,7 +311,8 @@ public abstract class TextToSpeechService extends Service {
}
};
Message msg = Message.obtain(this, runnable);
- // The obj is used to remove all callbacks from the given app in stop(String).
+ // The obj is used to remove all callbacks from the given app in
+ // stopForApp(String).
//
// Note that this string is interned, so the == comparison works.
msg.obj = speechItem.getCallingApp();
@@ -323,7 +330,7 @@ public abstract class TextToSpeechService extends Service {
*
* Called on a service binder thread.
*/
- public int stop(String callingApp) {
+ public int stopForApp(String callingApp) {
if (TextUtils.isEmpty(callingApp)) {
return TextToSpeech.ERROR;
}
@@ -331,8 +338,13 @@ public abstract class TextToSpeechService extends Service {
removeCallbacksAndMessages(callingApp);
// This stops writing data to the file / or publishing
// items to the audio playback handler.
- SpeechItem current = setCurrentSpeechItem(null);
- if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) {
+ //
+ // Note that the current speech item must be removed only if it
+ // belongs to the callingApp, else the item will be "orphaned" and
+ // not stopped correctly if a stop request comes along for the item
+ // from the app it belongs to.
+ SpeechItem current = maybeRemoveCurrentSpeechItem(callingApp);
+ if (current != null) {
current.stop();
}
@@ -341,6 +353,20 @@ public abstract class TextToSpeechService extends Service {
return TextToSpeech.SUCCESS;
}
+
+ public int stopAll() {
+ // Stop the current speech item unconditionally.
+ SpeechItem current = setCurrentSpeechItem(null);
+ if (current != null) {
+ current.stop();
+ }
+ // Remove all other items from the queue.
+ removeCallbacksAndMessages(null);
+ // Remove all pending playback as well.
+ mAudioPlaybackHandler.removeAllItems();
+
+ return TextToSpeech.SUCCESS;
+ }
}
interface UtteranceCompletedDispatcher {
@@ -412,6 +438,10 @@ public abstract class TextToSpeechService extends Service {
}
}
+ protected synchronized boolean isStopped() {
+ return mStopped;
+ }
+
protected abstract int playImpl();
protected abstract void stopImpl();
@@ -473,7 +503,7 @@ public abstract class TextToSpeechService extends Service {
Log.w(TAG, "Got empty text");
return false;
}
- if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH){
+ if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) {
Log.w(TAG, "Text too long: " + mText.length() + " chars");
return false;
}
@@ -485,6 +515,11 @@ public abstract class TextToSpeechService extends Service {
AbstractSynthesisCallback synthesisCallback;
mEventLogger.onRequestProcessingStart();
synchronized (this) {
+ // stop() might have been called before we enter this
+ // synchronized block.
+ if (isStopped()) {
+ return TextToSpeech.ERROR;
+ }
mSynthesisCallback = createSynthesisCallback();
synthesisCallback = mSynthesisCallback;
}
@@ -510,8 +545,13 @@ public abstract class TextToSpeechService extends Service {
synchronized (this) {
synthesisCallback = mSynthesisCallback;
}
- synthesisCallback.stop();
- TextToSpeechService.this.onStop();
+ if (synthesisCallback != null) {
+ // If the synthesis callback is null, it implies that we haven't
+ // entered the synchronized(this) block in playImpl which in
+ // turn implies that synthesis would not have started.
+ synthesisCallback.stop();
+ TextToSpeechService.this.onStop();
+ }
}
public String getLanguage() {
@@ -719,7 +759,7 @@ public abstract class TextToSpeechService extends Service {
return TextToSpeech.ERROR;
}
- return mSynthHandler.stop(intern(callingApp));
+ return mSynthHandler.stopForApp(intern(callingApp));
}
public String[] getLanguage() {
@@ -811,7 +851,7 @@ public abstract class TextToSpeechService extends Service {
synchronized (mAppToCallback) {
mAppToCallback.remove(packageName);
}
- mSynthHandler.stop(packageName);
+ mSynthHandler.stopForApp(packageName);
}
@Override