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

Commit eccd6fdd authored by Android (Google) Code Review's avatar Android (Google) Code Review
Browse files

Merge change 20673 into donut

* changes:
  Fix bug 2043140. A race condition is encountered when an application invokes shutdown() on its TextToSpeech object while is has speak() requests still running. Since the TTS service destructor releases the synthesizer resources and sets the corresponding synth reference to null, an NPE was observed. The fix consists in catching NPEs whenever the sNativeSynth object is accessed, and return the matching error for the call. This change is a "low risk" version of the fix for bug 2025765i (same issue) which was reverted because it was higher risk than this CL: it affected the logic of each call to sNativeSynth. This CL only sets an error code when an NPE is fired because sNativeSynth is null.
parents 3b5f4a84 69e67a3e
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -1032,7 +1032,7 @@ public class TextToSpeech {
            }
            try {
                String[] locStrings =  mITts.getLanguage();
                if (locStrings.length == 3) {
                if ((locStrings != null) && (locStrings.length == 3)) {
                    return new Locale(locStrings[0], locStrings[1], locStrings[2]);
                } else {
                    return null;
+79 −18
Original line number Diff line number Diff line
@@ -172,10 +172,18 @@ public class TtsService extends Service implements OnCompletionListener {
    @Override
    public void onDestroy() {
        super.onDestroy();

        // TODO replace the call to stopAll() with a method to clear absolutely all upcoming
        // uses of the native synth, including synthesis to a file, and delete files for which
        // synthesis was not complete.
        stopAll("");

        // Don't hog the media player
        cleanUpPlayer();

        if (sNativeSynth != null) {
            sNativeSynth.shutdown();
        }
        sNativeSynth = null;

        // Unregister all callbacks.
@@ -243,38 +251,70 @@ public class TtsService extends Service implements OnCompletionListener {


    private int setSpeechRate(String callingApp, int rate) {
        int res = TextToSpeech.ERROR;
        try {
            if (isDefaultEnforced()) {
            return sNativeSynth.setSpeechRate(getDefaultRate());
                res = sNativeSynth.setSpeechRate(getDefaultRate());
            } else {
            return sNativeSynth.setSpeechRate(rate);
                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) {
        return sNativeSynth.setPitch(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) {
        //Log.v("TtsService", "TtsService.isLanguageAvailable(" + lang + ", " + country + ", " +variant+")");
        return sNativeSynth.isLanguageAvailable(lang, country, 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("TtsService", "TtsService.setLanguage(" + lang + ", " + country + ", " + variant + ")");
        int res = TextToSpeech.ERROR;
        try {
            if (isDefaultEnforced()) {
            return sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
                res = sNativeSynth.setLanguage(getDefaultLanguage(), getDefaultCountry(),
                        getDefaultLocVariant());
            } else {
            return sNativeSynth.setLanguage(lang, country, variant);
                res = sNativeSynth.setLanguage(lang, country, variant);
            }
        } catch (NullPointerException e) {
            // synth will become null during onDestroy()
            res = TextToSpeech.ERROR;
        }
        return res;
    }


@@ -402,7 +442,12 @@ public class TtsService extends Service implements OnCompletionListener {
                }
                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 {
@@ -434,7 +479,8 @@ public class TtsService extends Service implements OnCompletionListener {


    /**
     * Stops all speech output and removes any utterances still in the queue globally.
     * 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;
@@ -451,7 +497,12 @@ public class TtsService extends Service implements OnCompletionListener {
                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 {
@@ -591,7 +642,12 @@ public class TtsService extends Service implements OnCompletionListener {
                        if (speechRate.length() > 0){
                            setSpeechRate("", Integer.parseInt(speechRate));
                        }
                        try {
                            sNativeSynth.speak(speechItem.mText, streamType);
                        } catch (NullPointerException e) {
                            // synth will become null during onDestroy()
                            Log.v("TtsService", " null synth, can't speak");
                        }
                    }
                } catch (InterruptedException e) {
                    Log.e("TtsService", "TTS speakInternalOnly(): tryLock interrupted");
@@ -660,7 +716,12 @@ public class TtsService extends Service implements OnCompletionListener {
                        if (speechRate.length() > 0){
                            setSpeechRate("", Integer.parseInt(speechRate));
                        }
                        try {
                            sNativeSynth.synthesizeToFile(speechItem.mText, speechItem.mFilename);
                        } catch (NullPointerException e) {
                            // synth will become null during onDestroy()
                            Log.v("TtsService", " null synth, can't synthesize to file");
                        }
                    }
                } catch (InterruptedException e) {
                    Log.e("TtsService", "TTS synthToFileInternalOnly(): tryLock interrupted");