Using addSpeech() in android TTS, you can link a certain text to a sound file. Then, the TTS engine plays the file instead of synthesizing the sound of the text (also in question at Android TTS(Text to Speech)'s addSpeech() and speak() can't play a sound file in the external storage from marshmallow(api 23) above, with Google TTS ). This is not working in Android 6.0 with TTS version 3.9.14 (and 3.10.10). So far, I did not see ant post with an answer as to why this is not working in Android 6.0. So I thought I'll provide more data on the issue that could help someone figure out the issue. (I had added this to the question in the link above, but moderators deleted it saying this is not an answer. They did not suggest a way to add more data like this except saying ask another question. Hence this question. In reality this is additional data on the same question that is not answered yet). so here goes.
In the manifest I have provided both read and write permissions to the app using TTS (which in turn uses Media Player) to play the voice files provided.
android:targetSdkVersion="22"
...
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Note that this works fine up to Android 5.0, but fails on android 6.0 (except when files in res/raw are used as voice files). Also when it fails it seems to give slightly different error in logcat depending on whether the voice file is on internal or external storage.
When the voice file in res/raw folder, TTS works fine playing the required voice file using the resource id (addSpeech (word, pkgName, resId))
When the voice file is located on external storage (/storage/sdcard0/pkgName/soundFiles/.. playing using TTS gives the EACCES failure log (for both amr and mp3 files).
09-08 16:57:17.514 1549-7830/? D/MediaPlayer: create failed: java.io.FileNotFoundException: /storage/emulated/0/pkgName/soundFiles/voice1.amr: open failed: EACCES (Permission denied) at libcore.io.IoBridge.open(IoBridge.java:487) at java.io.FileInputStream.(FileInputStream.java:76) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1115) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1066) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1003) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:983) at android.media.MediaPlayer.create(MediaPlayer.java:890 at android.speech.tts.AudioPlaybackQueueItem.run(AudioPlaybackQueueItem.java:58) at android.speech.tts.AudioPlaybackHandler$MessageLoop.run(AudioPlaybackHandler.java:134) at java.lang.Thread.run(Thread.java:818) Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied) at libcore.io.Posix.open(Native Method) at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186) at libcore.io.IoBridge.open(IoBridge.java:473) at java.io.FileInputStream.(FileInputStream.java:76) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1115) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1066) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1003) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:983) at android.media.MediaPlayer.create(MediaPlayer.java:890) at android.speech.tts.AudioPlaybackQueueItem.run(AudioPlaybackQueueItem.java:58) at android.speech.tts.AudioPlaybackHandler$MessageLoop.run(AudioPlaybackHandler.java:134) at java.lang.Thread.run(Thread.java:818
When the voice file is located on internal storage (/data/.../pkgName/soundFiles/.. playing the word using TTS gives the following error log (for both amr and mp3 files).
09-08 17:24:23.103 1549-32732/? D/MediaPlayer: create failed: java.io.IOException: setDataSource failed. at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1120) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1066) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1003) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:983) at android.media.MediaPlayer.create(MediaPlayer.java:890) at android.speech.tts.AudioPlaybackQueueItem.run(AudioPlaybackQueueItem.java:58) at android.speech.tts.AudioPlaybackHandler$MessageLoop.run(AudioPlaybackHandler.java:134) at java.lang.Thread.run(Thread.java:818)
As an experiment, created a MediaPlayer object in the same class where TTS is being used and played the same sound files that are failing with TTS. They played fine without any issue. So it looks like only the instantiation of MediaPlayer within TTS is having file permission issues.
Any help is appreciated.
Edit: Please note, runtime permissions have been granted, so this is not the issue. The problem is limited to the Google Text to Speech engine. Other engines work correctly.
The problem is, "MediaPlayer within TTS" is running in the other process, which does not have an access to the application files on actual (23+) Android versions.
In
addSpeech(CharSequence text, File file)
function, TextToSpeech internally converts File to Uri for interprocess communication usingUri.fromFile(file)
function, which produces the "file://" Uri, but for apps targeting Android 7.0, the Android framework enforces the StrictMode API policy that prohibits exposing file:// URIs outside your app, so this is completely useless !!!It produces the funny collision I have faced with: you can cache an utterance to file with
synthesizeToFile(CharSequence text, Bundle params, File file, String utteranceId)
TextToSpeech function, but you can not use this file later inaddSpeech(CharSequence text, File file)
.My workaround for this mess is as follows: