2

I'm trying to to get an app to play an mp3 from the sd card. I'm using the android sdk version 23, with Android studio on Windows 8. I'm running this on the nexus 5 emulator.

I'm requesting permission at runtime and I'm getting a FileNotFoundException if I deny the permission once but than grant the permission on the second request. If I restart the app, I'm able to play the music file and if I accept the permission request on the first try it also successfully plays the music without requiring a restart.

I've made a simple program that replicates the problem and posted it on github. You need an mp3 file in the /Music dir to run this app.

Here's the code where I ask for storage permission:

 private void CheckPermission() {

    int permissionCheck = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE);

    if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
        playSong();
    } else {
        // we don't have permission, request it
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                REQUEST_EXTERNAL_STORAGE_PERMISSION);

    }
}

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case REQUEST_EXTERNAL_STORAGE_PERMISSION:
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Permission Granted
                Toast.makeText(getApplicationContext(), "Storage access granted, touch screen to start music", Toast.LENGTH_SHORT)
                        .show();
            } else {
                // Permission Denied
                Toast.makeText(getApplicationContext(), "Storage access denied, can't load music", Toast.LENGTH_SHORT)
                        .show();
            }
            break;
        default:
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    }
}

Here's the relevant code snippet that's failing:

 Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String projection[] =
            {android.provider.MediaStore.Audio.Media.DATA, android.provider.MediaStore.Audio.Media.TITLE};
    Cursor cursor = this.getContentResolver().query(uri, projection, null, null, null);
    String songURI = new String();
    String title = new String();

    while (cursor.moveToNext()) {
        String data = cursor.getString(0);
        title = cursor.getString(1);
        // Note: Look for music folder in root drive.
        if (data.matches("^/storage/emulated/0/Music/.*")) {
            songURI = data;
            break;
        }
    }
    if (cursor != null) {
        cursor.close();
    }

    if (mediaPlayer != null) {
        mediaPlayer.stop();
        mediaPlayer.release();
        mediaPlayer = null;
    }

    mediaPlayer = MediaPlayer.create(getApplicationContext(), Uri.parse(songURI));

    try
    {
        mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
        mediaPlayer.start();
        Toast toast = Toast.makeText(getApplicationContext(), title, Toast.LENGTH_SHORT);
        toast.show();
    } catch (Exception e) {
        // TODO: handle exception
        e.printStackTrace();
        return;
    }

And here's stacktrace:

MediaPlayer: create failed: java.io.FileNotFoundException: /storage/emulated/0/Music/108-radiohead-house of cards-pms.mp3: open failed: EACCES (Permission denied) at libcore.io.IoBridge.open(IoBridge.java:452) at java.io.FileInputStream.(FileInputStream.java:76) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1095) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1074) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1028) at android.media.MediaPlayer.setDataSource(MediaPlayer.java:973) at android.media.MediaPlayer.create(MediaPlayer.java:880) at android.media.MediaPlayer.create(MediaPlayer.java:857) at android.media.MediaPlayer.create(MediaPlayer.java:836) at gunboat.com.mediaplayererror.FullscreenActivity.playSong(FullscreenActivity.java:190) at gunboat.com.mediaplayererror.FullscreenActivity.CheckPermission(FullscreenActivity.java:135) at gunboat.com.mediaplayererror.FullscreenActivity.access$300(FullscreenActivity.java:24) at gunboat.com.mediaplayererror.FullscreenActivity$5.onClick(FullscreenActivity.java:113) at android.view.View.performClick(View.java:5198) at android.view.View$PerformClick.run(View.java:21147) at android.os.Handler.handleCallback(Handler.java:739) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:148) at android.app.ActivityThread.main(ActivityThread.java:5417) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 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:438) at java.io.FileInputStream.(FileInputStream.java:76)  at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1095)  at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1074)  at android.media.MediaPlayer.setDataSource(MediaPlayer.java:1028)  at android.media.MediaPlayer.setDataSource(MediaPlayer.java:973)  at android.media.MediaPlayer.create(MediaPlayer.java:880)  at android.media.MediaPlayer.create(MediaPlayer.java:857)  at android.media.MediaPlayer.create(MediaPlayer.java:836)  at gunboat.com.mediaplayererror.FullscreenActivity.playSong(FullscreenActivity.java:190)  at gunboat.com.mediaplayererror.FullscreenActivity.CheckPermission(FullscreenActivity.java:135)  at gunboat.com.mediaplayererror.FullscreenActivity.access$300(FullscreenActivity.java:24)  at gunboat.com.mediaplayererror.FullscreenActivity$5.onClick(FullscreenActivity.java:113)  at android.view.View.performClick(View.java:5198)  at android.view.View$PerformClick.run(View.java:21147)  at android.os.Handler.handleCallback(Handler.java:739)  at android.os.Handler.dispatchMessage(Handler.java:95)  at android.os.Looper.loop(Looper.java:148)  at android.app.ActivityThread.main(ActivityThread.java:5417) 

1 Answers1

1

Just dealt with this today. You need to restart your application after the permission is granted. I use something like this to accomplish the restart:

private static final int REQUEST_CODE_READ_EXTERNAL_STORAGE = 1;

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    switch (requestCode) {
        case REQUEST_CODE_READ_EXTERNAL_STORAGE:
            if (grantResults.length >= 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // Schedule start after 1 second
                PendingIntent pi = PendingIntent.getActivity(this, 0, getIntent(), PendingIntent.FLAG_CANCEL_CURRENT);
                AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
                am.set(AlarmManager.RTC, System.currentTimeMillis() + 1000, pi);

                // Stop now
                System.exit(0);
            } else {
                Toast.makeText(this, R.string.error_external_storage_unavailable, Toast.LENGTH_LONG).show();
                finish();
            }

            break;
    }

    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}

Here one source I used to come to this conclusion: Application not able to access SD card when WRITE_EXTERNAL_STORAGE permission is granted at run time

The reason for the restart is that when the READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE permissions are granted the user account for your application gets added to a new security group. This change doesn't take effect while the application process is still running.

Community
  • 1
  • 1
Cory Charlton
  • 8,868
  • 4
  • 48
  • 68
  • Thanks for your reply. I had thought the next step is restarting the app but that's generally something I would try to avoid unless there's no other option. Is this expected behaviour or a bug? Looking at the [documentation](http://developer.android.com/reference/android/support/v4/app/ActivityCompat.html) it says some permissions will require an app restart but that "the system will recreate the activity stack before delivering the result to your onRequestPermission" – Flea Circus Feb 11 '16 at 10:14
  • @FleaCircus it's not really clear if it's a bug or not but the part you quoted suggests to me that Android should be handling the restart automatically. Unfortunately my only Marshmallow device is the emulator so this might work differently on real devices (although the numerous posts I found suggest it doesn't). – Cory Charlton Feb 11 '16 at 17:43
  • I'm in the same boat, I only have access to an emulator, I'm going to mark the question as answered as its a workable solution but I'm going to open a bug report on the Android project and if there's any further info I'll post it here. – Flea Circus Feb 11 '16 at 17:51