diff options
| -rw-r--r-- | core/java/android/speech/tts/TextToSpeechService.java | 41 |
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(); } |