Android playing resource files from internal storage causes MediaPlayer.prepare to give IOException
Asked Answered
W

2

11

My app plays audio resource files from the internal directory designated for my app (/data/data/com...). It seems to download the files to that location okay, setDataSource(String path) doesn't throw any exceptions, but MediaPlayer.prepare() throws IOException.The same code works on the SD card. Why is this happening?

EDIT:

Let's assume this is the code; it's simpler than my code and it throws the same exception:

package com.app.MediaPlayerTest;

public class MediaTest extends Activity {
    MediaPlayer mp;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        DownloadFiles();
        MusicPlay();
    }

    public void DownloadFiles() {
        //Downloads Files
    }

    public void MusicPlay()
    {
            try {
                mp.setDataSource("/data/data/com.app.pronounce/winds.mp3");
            } catch (IllegalArgumentException e1) {
                e1.printStackTrace();
            } catch (IllegalStateException e1) {
                e1.printStackTrace();
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        try {
            mp.prepare();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mp.setLooping(true);
        mp.start();
    }
}

As for the stack trace:

(mutexes: tll=0 tsl=0 tscl=0 ghl=0 hwl=0 hwll=0)
"main" prio=5 tid=1 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x4001f1a8 self=0xce48
  | sysTid=338 nice=0 sched=0/0 cgrp=bg_non_interactive handle=-1345006528
  | schedstat=( 151460588 425586896 45 )
  at android.os.BinderProxy.transact(Native Method)
  at android.app.ActivityManagerProxy.handleApplicationCrash(ActivityManagerNative.java:2547)
  at com.android.internal.os.RuntimeInit$UncaughtHandler.uncaughtException(RuntimeInit.java:76)
  at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:854)
  at java.lang.ThreadGroup.uncaughtException(ThreadGroup.java:851)
  at dalvik.system.NativeStart.main(Native Method)

"Binder Thread #2" prio=5 tid=8 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x40512b30 self=0x156e90
  | sysTid=346 nice=0 sched=0/0 cgrp=default handle=1570912
  | schedstat=( 4357682 930487 3 )
  at dalvik.system.NativeStart.run(Native Method)

"Binder Thread #1" prio=5 tid=7 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x40512a68 self=0x17f578
  | sysTid=345 nice=0 sched=0/0 cgrp=bg_non_interactive handle=604904
  | schedstat=( 6939806 13372136 3 )
  at dalvik.system.NativeStart.run(Native Method)

"Compiler" daemon prio=5 tid=6 VMWAIT
  | group="system" sCount=1 dsCount=0 obj=0x4050eba8 self=0x938c8
  | sysTid=344 nice=0 sched=0/0 cgrp=bg_non_interactive handle=1099136
  | schedstat=( 4770066 33579300 5 )
  at dalvik.system.NativeStart.run(Native Method)

"JDWP" daemon prio=5 tid=5 VMWAIT
  | group="system" sCount=1 dsCount=0 obj=0x4050eaf8 self=0x10c3c0
  | sysTid=343 nice=0 sched=0/0 cgrp=bg_non_interactive handle=1098624
  | schedstat=( 14899224 33240040 20 )
  at dalvik.system.NativeStart.run(Native Method)

"Signal Catcher" daemon prio=5 tid=4 RUNNABLE
  | group="system" sCount=0 dsCount=0 obj=0x4050ea38 self=0x93570
  | sysTid=342 nice=0 sched=0/0 cgrp=bg_non_interactive handle=588000
  | schedstat=( 24278832 4707632 7 )
  at dalvik.system.NativeStart.run(Native Method)

"GC" daemon prio=5 tid=3 VMWAIT
  | group="system" sCount=1 dsCount=0 obj=0x4050e990 self=0x8f720
  | sysTid=341 nice=0 sched=0/0 cgrp=bg_non_interactive handle=1099336
  | schedstat=( 791698 556969 3 )
  at dalvik.system.NativeStart.run(Native Method)

"HeapWorker" daemon prio=5 tid=2 VMWAIT
  | group="system" sCount=1 dsCount=0 obj=0x4050e8d8 self=0x10c740
  | sysTid=340 nice=0 sched=0/0 cgrp=bg_non_interactive handle=1357728
  | schedstat=( 211702049 225986921 9 )
  at dalvik.system.NativeStart.run(Native Method)
Wastebasket answered 28/1, 2011 at 22:43 Comment(0)
T
23

MediaPlayer requires that the file being played has world-readable permissions. You can view the permissions of the file with the following command in adb shell:

ls -al /data/data/com.mypackage/myfile

You will probably see "-rw------", which means that only the owner (your app, not MediaPlayer) has read/write permissions.

Note: Your phone must be rooted in order to use the ls command without specifying the file (in the internal memory).

If your phone is rooted, you can add world-read permissions in adb shell with the following command:

chmod o+r /data/data/com.mypackage/myfile

If you need to modify these permissions programmatically (requires rooted phone!), you can use the following command in your app code:

Runtime.getRuntime().exec("chmod o+r /data/data/com.mypackage/myfile");

or

Runtime.getRuntime().exec("chmod 777 /data/data/com.mypackage/myfile");

Which is basically a linux command. See https://help.ubuntu.com/community/FilePermissions for more on chmod.

EDIT: Found another simple approach here (useful for those without rooted phones). Since the application owns the file, it can create a file descriptor and pass that to mediaPlayer.setDataSource():

FileInputStream fileInputStream = new FileInputStream("/data/data/com.mypackage/myfile");
mediaPlayer.setDataSource(fileInputStream.getFD());

This approach avoids the permission issue completely.

Turnout answered 28/3, 2011 at 23:33 Comment(3)
Thanks, I was really confused on what was happening. I'll set it to world-readable mode when I create the file.Wastebasket
@WRU4Android That approach works great, unless you are creating your files with MediaRecorder. See #3038974.Turnout
No, I'm not using MediaRecorder, I'm just downloading them from online. But thanks for the tip.Wastebasket
S
3

I know this question is older than dirt, but it helped me flush out my issue. The following works fine:

      FileOutputStream outStream= openFileOutput("movie.mp4", MODE_WORLD_READABLE);

The world readable flag is the important part here.

Seaman answered 6/5, 2013 at 2:42 Comment(3)
Be careful with that approach, you're letting other apps access your file (provided that: 1) this is in the Internal Storage, 2) They know your Fully Qualified Package Name, 3) They know the exact file name).Duplication
What would you suggest?Seaman
I have the exact same thing, only using a "cryptic-pseudo-random" filename. I haven't found any other decent solution.Duplication

© 2022 - 2024 — McMap. All rights reserved.