summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/speech/tts/TextToSpeechService.java41
1 files changed, 30 insertions, 11 deletions
diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java
index c645f4057335..10d7911316ac 100644
--- a/core/java/android/speech/tts/TextToSpeechService.java
+++ b/core/java/android/speech/tts/TextToSpeechService.java
@@ -502,10 +502,28 @@ public abstract class TextToSpeechService extends Service {
return mCurrentSpeechItem;
}
- private synchronized SpeechItem setCurrentSpeechItem(SpeechItem speechItem) {
- SpeechItem old = mCurrentSpeechItem;
+ private synchronized boolean setCurrentSpeechItem(SpeechItem speechItem) {
+ // Do not set as current if the item has already been flushed. The check is
+ // intentionally put inside this synchronized method. Specifically, the following
+ // racy sequence between this method and stopForApp() needs to be avoided.
+ // (this method) (stopForApp)
+ // 1. isFlushed
+ // 2. startFlushingSpeechItems
+ // 3. maybeRemoveCurrentSpeechItem
+ // 4. set mCurrentSpeechItem
+ // If it happens, stop() is never called on the item. The guard by synchornized(this)
+ // ensures that the step 3 cannot interrupt between 1 and 4.
+ if (speechItem != null && isFlushed(speechItem)) {
+ return false;
+ }
mCurrentSpeechItem = speechItem;
- return old;
+ return true;
+ }
+
+ private synchronized SpeechItem removeCurrentSpeechItem() {
+ SpeechItem current = mCurrentSpeechItem;
+ mCurrentSpeechItem = null;
+ return current;
}
private synchronized SpeechItem maybeRemoveCurrentSpeechItem(Object callerIdentity) {
@@ -527,7 +545,7 @@ public abstract class TextToSpeechService extends Service {
// Don't process any more speech items
getLooper().quit();
// Stop the current speech item
- SpeechItem current = setCurrentSpeechItem(null);
+ SpeechItem current = removeCurrentSpeechItem();
if (current != null) {
current.stop();
}
@@ -561,12 +579,12 @@ public abstract class TextToSpeechService extends Service {
Runnable runnable = new Runnable() {
@Override
public void run() {
- if (isFlushed(speechItem)) {
- speechItem.stop();
- } else {
- setCurrentSpeechItem(speechItem);
+ if (setCurrentSpeechItem(speechItem)) {
speechItem.play();
- setCurrentSpeechItem(null);
+ removeCurrentSpeechItem();
+ } else {
+ // The item is alreadly flushed. Stopping.
+ speechItem.stop();
}
}
};
@@ -600,7 +618,8 @@ public abstract class TextToSpeechService extends Service {
return TextToSpeech.ERROR;
}
- // Flush pending messages from callerIdentity
+ // Flush pending messages from callerIdentity.
+ // See setCurrentSpeechItem on a subtlety around a race condition.
startFlushingSpeechItems(callerIdentity);
// This stops writing data to the file / or publishing
@@ -634,7 +653,7 @@ public abstract class TextToSpeechService extends Service {
startFlushingSpeechItems(null);
// Stop the current speech item unconditionally .
- SpeechItem current = setCurrentSpeechItem(null);
+ SpeechItem current = removeCurrentSpeechItem();
if (current != null) {
current.stop();
}