Search code examples
androidringtoneandroid-permissionssecurityexception

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.


Solution

  • 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));