97

I'm trying to access media files (music) on the users device to play them; an easy "hello world"-music player app.

I've followed some tutorials and they basically give the same code. But it won't work; it keeps crashing and telling me:

error.....
Caused by: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=27696, uid=10059 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
....

Now, this is my manifest file:

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    package="slimsimapps.troff" >

    <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
</manifest>

This is my Java-method:

public void initialize() {
    ContentResolver contentResolver = getContentResolver();
    Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    Cursor cursor = contentResolver.query(uri, null, null, null, null);
    if (cursor == null) {
        // query failed, handle error.
    } else if (!cursor.moveToFirst()) {
        // no media on the device
    } else {
        do {
            addSongToXML(cursor);
        } while (cursor.moveToNext());
    }
}

I have tried:

To put this at different places in the manifest file:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE”/>

To add android:maxSdkVersion at Read external storage premission:

<uses-permission
    android:name="android.permission.READ_EXTERNAL_STORAGE"
    android:maxSdkVersion="21" />

To put this in the manifest / application / activity-tag:

android:exported=“true”

To put grantUriPremission between uri and cursro in the javamethod:

grantUriPermission(null, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);

To use this, it won't crash, but the cursor becomes null:

uri = MediaStore.Audio.Media.getContentUri("EXTERNAL_CONTENT_URI”);

To use INTERNAL content uri, this works as expected, but it only gives "OS-sounds" such as shutter-sound, low-battery-sound, button-click and such:

uri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;

Pleas help, this should not be a hard problem i know, but i feel like such a beginner!

I have read and tried (or considered them to be not applicable for my problem):

Stack trace:

09-08 06:59:36.619    2009-2009/slimsimapps.troff D/AndroidRuntime﹕ Shutting down VM
    --------- beginning of crash
09-08 06:59:36.619    2009-2009/slimsimapps.troff E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: slimsimapps.troff, PID: 2009
    java.lang.IllegalStateException: Could not execute method for android:onClick
            at android.view.View$DeclaredOnClickListener.onClick(View.java:4452)
            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: java.lang.reflect.InvocationTargetException
            at java.lang.reflect.Method.invoke(Native Method)
            at android.view.View$DeclaredOnClickListener.onClick(View.java:4447)
            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: java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaProvider uri content://media/external/audio/media from pid=2009, uid=10059 requires android.permission.READ_EXTERNAL_STORAGE, or grantUriPermission()
            at android.os.Parcel.readException(Parcel.java:1599)
            at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
            at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:135)
            at android.content.ContentProviderProxy.query(ContentProviderNative.java:421)
            at android.content.ContentResolver.query(ContentResolver.java:491)
            at android.content.ContentResolver.query(ContentResolver.java:434)
            at slimsimapps.troff.MainActivity.initialize(MainActivity.java:106)
            at slimsimapps.troff.MainActivity.InitializeExternal(MainActivity.java:80)
            at java.lang.reflect.Method.invoke(Native Method)
            at android.view.View$DeclaredOnClickListener.onClick(View.java:4447)
            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)
    --------- beginning of system
Community
  • 1
  • 1
Slim Sim
  • 1,153
  • 1
  • 7
  • 14
  • Strange that error is shown when you have not given this line in manifest are you trying to read some files which is protected by some other apps? – Sunil Sunny Sep 07 '15 at 05:42
  • On which api level are you trying this ? – Sharp Edge Sep 07 '15 at 05:48
  • Did you tried cleaning the project. ? – Sunil Sunny Sep 07 '15 at 05:50
  • @sunilsunny I'm not trying to read som files that are protected, not that I know of anyway, just a simple media player. Yes, I have tried to clean it, i have tried to restart computer, I have tried to generate a signed APK and publish it to google Play and access it as a tester, with no luck.... – Slim Sim Sep 07 '15 at 14:27
  • @SharpEdge ; My AVD is the standard Nexus 5, api 23. My module gradle has: compileSdkVersion 23 buildToolsVersion "23.0.0" minSdkVersion 14 targetSdkVersion 23 So i would say 23. – Slim Sim Sep 07 '15 at 14:29
  • can you open your specific audio with any other media player? is the file drm protected? or is located in a protected directory where only super-user has access to? – k3b Sep 07 '15 at 16:03
  • @k3b I don't have any other audio on my AVD, but I would like for my app to say, nrSongs=0, instead of crashing. On my mobile device i have songs, which I can open with both Google Play Music, and another music player I downloaded from the play store. so, Yes - I Can. "Is the file drm protected?" - I don't know, I just want to access all the music files that I'm allowed to access... like a music player... "Is it located in a protected directory..." same anser as before. Note; I'm not including the audio as part of the app, I want to play the audio from the users media library. – Slim Sim Sep 08 '15 at 05:09

10 Answers10

98

You have two solutions for your problem. The quick one is to lower targetApi to 22 (build.gradle file). Second is to use new and wonderful ask-for-permission model:

if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {

    // Should we show an explanation?
    if (shouldShowRequestPermissionRationale(
            Manifest.permission.READ_EXTERNAL_STORAGE)) {
        // Explain to the user why we need to read the contacts
    }

    requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);

    // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is an
    // app-defined int constant that should be quite unique

    return;
}

Sniplet found here: https://developer.android.com/training/permissions/requesting.html

Solutions 2: If it does not work try this:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
    && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
        REQUEST_PERMISSION);

return;

}

and then in callback

@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == REQUEST_PERMISSION) {
    if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        // Permission granted.
    } else {
        // User refused to grant permission.
    }
}
}

that is from comments. thanks

MindRoasterMir
  • 324
  • 1
  • 2
  • 18
piotrpo
  • 12,398
  • 7
  • 42
  • 58
  • 1
    Yes, of course! The new permission model Google introduced with the latest Android! Thank you, I will test this as soon as possible and mark it correct if it works! (and i was beginning to think this was forgotten!) – Slim Sim Sep 17 '15 at 15:47
  • 1
    The permission that is being requested should be `READ_EXTERNAL_STORAGE`. Like in [this answer](http://stackoverflow.com/a/35720050/1432239). – maclir Mar 01 '16 at 10:12
  • swap != PackageManager.READ_EXTERNAL_STORAGE for != PackageManager.PERMISSION_GRANTED – Renascienza Nov 10 '16 at 17:51
  • 1
    @Renascienza, yes you're right. Someone edited this answer in wrong way. – piotrpo Nov 14 '16 at 18:31
  • where does checkSelfPermissions() come from, im getting a red line error here? – TheQ Apr 12 '17 at 04:41
  • 2
    What is MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE ? – Solo Aug 05 '17 at 10:32
  • @Solo Just an int constant used to identify permission granted event in the callback. This value is returned as a param to onRequestPermissionResult(...) method. – piotrpo Aug 06 '17 at 16:04
17

You have to ask for the permission at run time:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
        && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
    ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
            REQUEST_PERMISSION);
    dialog.dismiss();
    return;
}

And in the callback below you can access the storage without a problem.

@Override
public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == REQUEST_PERMISSION) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Permission granted.
        } else {
            // User refused to grant permission.
        }
    }
}
maclir
  • 3,218
  • 26
  • 39
9

Step1: add permission on android manifest.xml

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

Step2: onCreate() method

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

    if (permissionCheck != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, MY_PERMISSIONS_REQUEST_READ_MEDIA);
    } else {
        readDataExternal();
    }

Step3: override onRequestPermissionsResult method to get callback

 @Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_REQUEST_READ_MEDIA:
            if ((grantResults.length > 0) && (grantResults[0] == PackageManager.PERMISSION_GRANTED)) {
                readDataExternal();
            }
            break;

        default:
            break;
    }
}

Note: readDataExternal() is method to get data from external storage.

Thanks.

sonnv1368
  • 1,547
  • 12
  • 17
6

Starting with Android 13, you will need to use READ_MEDIA_IMAGES

<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

So you need to programmatically ask for READ_EXTERNAL_STORAGE permission for API level lower than 33 and ask for READ_MEDIA_IMAGES permission for API level equal or higher than 33, here is an example to use the new api.

binding.btnchangeProfilePicture.setOnClickListener {
                val readImagePermission =
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU)
                        Manifest.permission.READ_MEDIA_IMAGES else Manifest.permission.READ_EXTERNAL_STORAGE
                requestPermission.launch(readImagePermission)
            }
    
private val requestPermission = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
                    if (isGranted) {
                       
                    } else {
                        
                    }
                }
Shurvir Mori
  • 2,288
  • 1
  • 17
  • 29
Mohamed AbdelraZek
  • 2,503
  • 4
  • 25
  • 36
5

I also had a similar error log and here's what I did-

  1. In onCreate method we request a Dialog Box for checking permissions

    ActivityCompat.requestPermissions(MainActivity.this,
    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},1);
    
  2. Method to check for the result

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
        case 1: {
            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // permission granted and now can proceed
             mymethod(); //a sample method called
    
            } else {
    
                // permission denied, boo! Disable the
                // functionality that depends on this permission.
                Toast.makeText(MainActivity.this, "Permission denied to read your External storage", Toast.LENGTH_SHORT).show();
            }
            return;
        }
        // add other cases for more permissions
        }
    }
    

The official documentation to Requesting Runtime Permissions

1

Has your problem been resolved? What is your target SDK? Try adding android;maxSDKVersion="21" to <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Jerry Ejonavi
  • 56
  • 1
  • 7
1

In 2021 I am facing this issue So I will solve this problem using the manifest file I add one tag inside application:

<application 
android:requestLegacyExternalStorage="true"
/>

NOTE: This only work until Android 10, see more information here

juzraai
  • 5,693
  • 8
  • 33
  • 47
Aakash Kumar
  • 111
  • 8
0

http://developer.android.com/reference/android/provider/MediaStore.Audio.AudioColumns.html

Try changing the line contentResolver.query

//change it to the columns you need
String[] columns = new String[]{MediaStore.Audio.AudioColumns.DATA}; 
Cursor cursor = contentResolver.query(uri, columns, null, null, null);

public static final String MEDIA_CONTENT_CONTROL

Not for use by third-party applications due to privacy of media consumption

http://developer.android.com/reference/android/Manifest.permission.html#MEDIA_CONTENT_CONTROL

try to remove the <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL"/> first and try again?

Derek Fung
  • 8,171
  • 1
  • 25
  • 28
  • Thanks, I tried your suggestion but it crashed on the same spot... I'm using a Mac, can that be a problem? – Slim Sim Sep 07 '15 at 14:39
  • updated, try to remove `` and try? It may create strange behaviour because your app request for an permission not allowed – Derek Fung Sep 07 '15 at 15:25
  • thanks, i tried to remove that permission, but it crashes at the same spot with the same error... – Slim Sim Sep 08 '15 at 05:11
0

Please Check below code that using that You can find all Music Files from sdcard :

public class MainActivity extends Activity{

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_animations);
    getAllSongsFromSDCARD();

}

public void getAllSongsFromSDCARD() {
    String[] STAR = { "*" };
    Cursor cursor;
    Uri allsongsuri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0";

    cursor = managedQuery(allsongsuri, STAR, selection, null, null);

    if (cursor != null) {
        if (cursor.moveToFirst()) {
            do {
                String song_name = cursor
                        .getString(cursor
                                .getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME));
                int song_id = cursor.getInt(cursor
                        .getColumnIndex(MediaStore.Audio.Media._ID));

                String fullpath = cursor.getString(cursor
                        .getColumnIndex(MediaStore.Audio.Media.DATA));

                String album_name = cursor.getString(cursor
                        .getColumnIndex(MediaStore.Audio.Media.ALBUM));
                int album_id = cursor.getInt(cursor
                        .getColumnIndex(MediaStore.Audio.Media.ALBUM_ID));

                String artist_name = cursor.getString(cursor
                        .getColumnIndex(MediaStore.Audio.Media.ARTIST));
                int artist_id = cursor.getInt(cursor
                        .getColumnIndex(MediaStore.Audio.Media.ARTIST_ID));
                System.out.println("sonng name"+fullpath);
            } while (cursor.moveToNext());

        }
        cursor.close();
    }
}


}

I have also added following line in the AndroidManifest.xml file as below:

<uses-sdk
    android:minSdkVersion="16"
    android:targetSdkVersion="17" />

<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Rajan Bhavsar
  • 1,977
  • 11
  • 25
  • Thanks, I have tried something similar before, but I tried your code and it crashes on "cursor = managedQuery(allsongsuri, STAR, selection, null, null);", same error as before. By the way; managedQuery is deprecated, but it still crashes if i change it to "contentResolver.query", which have the same input parameters... – Slim Sim Sep 07 '15 at 14:32
  • Hi I am able to get All sdcadr Songs using above code. with Android Version 4.2.2 Device and also able to with Version 5.0.2. In which You are trying. What Error you got please describe with Android Version – Rajan Bhavsar Sep 08 '15 at 04:19
  • I have updated the question with the stack-trace, hope that helps. What do you mean with Android Version? (I'm using apk 23) – Slim Sim Sep 08 '15 at 05:18
  • Ok Let me Give Full Source code and Check Out that again. – Rajan Bhavsar Sep 08 '15 at 05:56
  • if you want to test for yourself, download the zip here: https://drive.google.com/open?id=0B2AV8VRk9HUeVzFXSWVRZU9TRUk when you run it, press the left initialize-button, to generate the crach. – Slim Sim Sep 08 '15 at 15:42
0

In addition of all answers. You can also specify the minsdk to apply with this annotation

@TargetApi(_apiLevel_)

I used this in order to accept this request even my minsdk is 18. What it does is that the method only runs when device targets "_apilevel_" and upper. Here's my method:

    @TargetApi(23)
void solicitarPermisos(){

    if (ContextCompat.checkSelfPermission(this,permiso)
            != PackageManager.PERMISSION_GRANTED) {

        // Should we show an explanation?
        if (shouldShowRequestPermissionRationale(
                Manifest.permission.READ_EXTERNAL_STORAGE)) {
            // Explain to the user why we need to read the contacts
        }

        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                1);

        // MY_PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE is an
        // app-defined int constant that should be quite unique

        return;
    }

}
Hanako
  • 1,637
  • 1
  • 13
  • 16