Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a65c62ac authored by Narayan Kamath's avatar Narayan Kamath
Browse files

Fix two TTS bugs.

(a) Fix a null pointer exception, caused by a race condition
between stop / start calls.
(b) Fix a deadlock observed when multiple apps call stop() when
an item from one of those apps is currently being processed.

bug:5253061

Change-Id: I78533aecfda028588ce6aedb041009bc0a6f4620
parent 38aac047
Loading
Loading
Loading
Loading
+56 −16
Original line number Diff line number Diff line
@@ -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();
@@ -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,9 +545,14 @@ public abstract class TextToSpeechService extends Service {
            synchronized (this) {
                synthesisCallback = mSynthesisCallback;
            }
            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() {
            return getStringParam(Engine.KEY_PARAM_LANGUAGE, mDefaultLocale[0]);
@@ -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