Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

External Storage Permission Issue with MediaProvider / Ring Tones

Some of my users have reported to Google Play the following error when trying to select a ringtone in my app. (there's more to it, but it's not relevant)

java.lang.SecurityException: Permission Denial: 
reading com.android.providers.media.MediaProvider 
uri content://media/external/audio/media 
from pid=5738, uid=10122 requires android.permission.READ_EXTERNAL_STORAGE

I assume this issue is happening due to certain tones that are on external storage. I don't want to include the READ_EXTERNAL_STORAGE permission in my app unless I absolutely have to.

Is there a way to circumvent the issue and just exclude any tones that may be on the external storage?

Note: I'm getting ringtones with RingtoneManager and convert them between String and Uri. No other code is touching the user's media.

Also, I do not have a line number since the stacktrace is from obfuscated code and re-mapping the stack trace did not provide line number.

like image 522
Reed Avatar asked Jan 17 '14 21:01

Reed


People also ask

How do I grant external storage permissions on Android?

On the Settings > Privacy > Permission manager > Files and media page, each app that has the permission is listed under Allowed for all files. If your app targets Android 11, keep in mind that this access to "all files" is read-only.

How do I get read external permissions on Android?

To read and write data to external storage, the app required WRITE_EXTERNAL_STORAGE and READ_EXTERNAL_STORAGE system permission. These permissions are added to the AndroidManifest. xml file. Add these permissions just after the package name.

What is external storage permission?

When an app is granted storage permission, it can access the device storage at any time. This means it can upload personal files or even delete sensitive information from the device, so it's better to think twice before giving storage permission to untrusted apps, as it can be harmful.


1 Answers

Just had the same problem and came up with the following solution:

private Cursor createCursor()
{
    Uri uri = MediaStore.Audio.Media.INTERNAL_CONTENT_URI;

    String[] columns = new String[]
    {
        MediaStore.Audio.Media._ID,
        MediaStore.Audio.Media.TITLE,
        MediaStore.Audio.Media.TITLE_KEY
    };

    String filter = createBooleanFilter(MediaStore.Audio.AudioColumns.IS_ALARM);
    String order = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;

    return getContext().getContentResolver().query(uri, columns, filter, null, order);
}

private String createBooleanFilter(String... columns)
{
    if(columns.length > 0)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("(");
        for(int i = columns.length - 1; i > 0; i--)
        {
            sb.append(columns[i]).append("=1 or ");
        }
        sb.append(columns[0]);
        sb.append(")");
        return sb.toString();
    }
    return null;
}

To get the Uri of a ringtone you need to combine the INTERNAL_CONTENT_URI with the _ID column value, you can do this by using ContentUris class:

Uri uri = ContentUris.withAppendedId(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, cursor.getLong(0));
like image 68
atomicode Avatar answered Sep 28 '22 05:09

atomicode