4

In the past, we could use code below to set an audio file as ringtone:

ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, musicFile.getAbsolutePath());
values.put(MediaStore.MediaColumns.TITLE, "my music");
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp3");
values.put(MediaStore.MediaColumns.SIZE, 215454);
values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
values.put(MediaStore.Audio.Media.IS_NOTIFICATION, false); // true for notification sound
values.put(MediaStore.Audio.Media.IS_ALARM, false);
values.put(MediaStore.Audio.Media.IS_MUSIC, false);

Uri uri = MediaStore.Audio.Media.getContentUriForPath(musicFile.getAbsolutePath());
Strint where = MediaStore.MediaColumns.DATA + "=\""
                + newSoundFile.getAbsolutePath() + "\"";
getContentResolver().delete(uri, where, null);
Uri newUri = getContentResolver().insert(uri, values);
RingtoneManager.setActualDefaultRingtoneUri(
        RingtonesPlaying.this, RingtoneManager.TYPE_RINGTONE, newUri);

However, if we run the code above Nougat(7.0, API 24), we will receive a SecurityException for getContentResolver().insert() that we don't have permission of MANAGE_DOCUMENTS, which will always be thrown even if we declare this permission in AndroidManifest.

I really want to set audio file as ringtone since I want users of my app to have the ability of customizing notification sound. In fact we can use builder.setSound(Uri.fromFile(musicFile)) before N for Notification, but this approach is also forbidden on N and will throw a FileUriExposedException.

ywwynm
  • 11,573
  • 7
  • 37
  • 53
  • "this approach is also forbidden on N and will throw a `FileUriExposedException`" -- use `FileProvider` and `FileProvider.getUriForFile()` instead of `Uri.fromFile()`. – CommonsWare Sep 02 '16 at 11:54
  • @CommonsWare The audio file is chosen by user, I don't want to limit folders that user can only choose their music from. So if I use `FileProvider.getUriForFile()`, I must specify those folders, which is very useless under this situation. – ywwynm Sep 02 '16 at 12:53
  • "The audio file is chosen by user" -- when the user chose the audio from the `MediaStore`, you had a `Uri` to the sound in the `MediaStore`. I don't know how long-lived that `Uri` is. Either use it directly, or make a copy of the audio to `getCacheDir()` and use `FileProvider`, or use `FileProvider` with an `external-path` configuration. – CommonsWare Sep 02 '16 at 13:50
  • @CommonsWare Could you please post an answer here? Since the `FileProvider` API is complete new, I don't know how to use it to achieve my goal. I think there are also a lot of developers who don't know much about it, either. So if you can explain it to us, we will thank you very much. – ywwynm Sep 03 '16 at 02:11
  • @CommonsWare Following your suggestion, I'm using a FileProvider with an external-path to set the sound for a notification -using NotificationCompat.Builder.setSound()- but it results in Permission Denial. Should I give any kind of permission to the NotificationManager?? And what's more important, why is setting the notification sound a security risk? What am I exposing here? – jmart Sep 06 '16 at 23:56
  • @jmart: "but it results in Permission Denial" -- ummm... that's an interesting point. Normally, we pass these `Uri` values around via `Intents`, and we can use `FLAG_GRANT_READ_URI_PERMISSION` to allow the responder to have access to our content. I'll have to look into this. "why is setting the notification sound a security risk?" -- it's not, but with `FileProvider`, content is granted on a per-request basis. The content is not publicly readable by all comers. – CommonsWare Sep 07 '16 at 00:01
  • 1
    @jmart: See [the answer that I just posted](http://stackoverflow.com/a/39375749/115145) or [the blog post of mine that the answer came from](https://commonsware.com/blog/2016/09/07/notifications-sounds-android-7p0-aggravation.html) for a roster of workarounds. – CommonsWare Sep 07 '16 at 17:12

0 Answers0