18

My app creates playlists in the android mediastore. All is well for api's including 28 however, api 29 seems to require additional permissions. Inserting a new playlist name and id works without issue. When it comes to inserting track id and play order, an access permission exception is thrown. In verifying the Uri, i found that when resolver.insert for API 29 the exception error is:

java.lang.SecurityException: myapp_name has no access to content://media/external_primary/audio/media/146

The code:

Uri exturi = MediaStore.Audio.Playlists.Members.getContentUri("external", playlist_id);
// exturi : content://media/external/audio/playlists/227/members

// values : audio_id=146 play_order=0
values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, play_order);
values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, audio_id);

try {
    resolver.insert(exturi, values);
} catch (Exception e) {
    e.printStackTrace();
}

Strange thing is that although inserting a new playlist into Mediastore works but adding tracks (track_id, play order) gives an access permission error

How to resolve this exception error for API 29?

Update Feb 2021: a small step forward, I am pretty sure I need to get Documenturi for the original uri but still gives me the access error. So the issue does not lie with accessing the tracks but with the uri itself.

doc_uri = MediaStore.getDocumentUri(context,playlist_members_uri);
java.lang.SecurityException: com.flyingdutchman.newplaylistmanager has no access to content://media/external/audio/playlists/130/members
Theo
  • 2,012
  • 1
  • 16
  • 29
  • don't use just external hard-coded, use the volume external primary. – greywolf82 Aug 07 '19 at 17:17
  • https://developer.android.com/training/data-storage/shared/media Try to use MediaStore.VOLUME_EXTERNAL_PRIMARY (On API <= 28, use VOLUME_EXTERNAL instead) – Khoa Nguyễn Jan 09 '20 at 09:04
  • @Khoa Nguyễn tried this to no avail – Theo Jan 09 '20 at 14:21
  • @Theo Any luck? Does your phone have an SD card? (I have crash reports of this, but can't reproduce) – now Jan 13 '20 at 20:22
  • 3
    I have the same issue. I can delete tracks from a playlist, but when adding I get the same exception on API 29. Have you found a solution yet? I would be very happy for help on this. – morja Jan 13 '20 at 23:44
  • It actually seems to be a known bug in Android 10: https://issuetracker.google.com/issues/139964268. Starring it may help in a faster fix... it still might take a long time until it is fixed though. Does anyone know if creating playlists via m3u is affected too? – morja Jan 14 '20 at 00:29
  • This happens to me as well when creating a new playlist. The playlist gets created, tho! – private static Jul 12 '20 at 19:37
  • Hey @morja, I am having exact same issue. Did you found a solution to that? I can delete songs, but when I try to add I get SecurityException. Thanks! – EvanBlack Jan 10 '21 at 18:56
  • 1
    @EvanBlack Hi. At the moment I do a workaround with file based m3u playlists. The system scans those and creates the playlists. The mayor downside is that the ids change every time the playlists are recreated. Thus some apps using the playlists need to be updated every time they change. – morja Jan 11 '21 at 19:17

8 Answers8

8

I think this is an Android 10 bug, so I've filed a report here: https://issuetracker.google.com/issues/147619577 (includes instructions for an emulator test case to reproduce it if that interests you). Please consider starring it to let the Android team know that it affects you.

From what I can tell, it only affects files on 'external' storage, like sdcards mounted on /storage/XXXX-XXXX

In the meantime, the only fix that some of my users were able to successfully apply is to move their music files to the internal storage (reboot and wait for the media scan to finish to be sure that MediaStore is up-to-date).

now
  • 4,772
  • 2
  • 24
  • 26
  • Thanks i will try music on internal memory – Theo Jan 14 '20 at 19:12
  • android 10 introduces a new storage concept: **Scoped storage**, If you are not comfortable with the Scoped Storage, add this flag to your manifestfile (but it's not recomanded, becuase it is going to be compulsory in next release of Android): **** – toumir Jan 28 '20 at 06:58
  • @toumir The bug also affect apps targeting API 28 or using requestLegacyExternalStorage="true", so this doesn't help. – now Jan 28 '20 at 17:21
  • @now not bug but this behavior affects apps targeting API 29 or +, API 28 and below works normally for me. – toumir Jan 29 '20 at 14:24
  • I'd be curious to see it. Could you share a project that works? – now Jan 30 '20 at 18:00
  • In API level 29, user needs to grant any actions regarding files that have been created by other apps. https://developer.android.com/training/data-storage/shared/media – laim2003 Nov 20 '20 at 12:37
1

in my further research for the answer, I came across this;

All about the media database (Mediastore) with android 11

Theo
  • 2,012
  • 1
  • 16
  • 29
1

Update for android 11. Worth noting that the media database has moved from

/data/data/com.android.providers.media

to

/data/data/com.google.android.providers.media.module

also the structures have changes significantly

before android 10/11 and android 11

Theo
  • 2,012
  • 1
  • 16
  • 29
  • I tried everything and it still won't work this way above API 28. I gave up and just made my own database using Room with Playlist and Song table. ScopedStorage ruined everything. – Vince VD Mar 26 '21 at 18:45
1
  1. Create playlist with uri "MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI", and the date row in external.db for the playlist is:
_id _display_name volume_name
308 New playlist.m3u external_primary

The playlist's volume name is "external_primary".

2.

  • Music file is under flash card
  • Music file's id in external.db is 278
  • The volume name of flash card is "1EDD-DDE0"

When add this music file to playlist, got below exception:
Exception message: java.lang.SecurityException: has no access to content://media/external_primary/audio/media/278

If I create playlist with uri MediaStore.Audio.Playlists.getContentUri("1edd-dde0"), then music can be successfully added to the playlist.

It seems that the reason is the mismatch of volume name between playlist and the music file to be added. Only when playlist's volume name is same to music file's, inserting operation can be complete.

wanted
  • 11
  • 1
  • I applied the suggestion but .... Uri uri_to_use = MediaStore.Audio.Playlists.getContentUri("0F16-2701"); -gives : content://media/0F16-2701/audio/playlists, and results in java.lang.IllegalArgumentException: Volume 0F16-2701 not found – Theo Apr 09 '21 at 11:48
  • I mean, you should make the playlist on the same volume as the song you want to add. In your case, you should create your playlist on "0F16-2701", then you can add the songs located in "0F16-2701" to the playlist. – wanted Apr 14 '21 at 07:38
  • I appreciate your interest. I think I did as you suggested as shown in my uri_to_use example. What I do not understand is why you would need the volume name at all as an android playlist is simply an entry with an entry_id and name in the files table with media_type=4. I started with a clean emulator, createe 1 android playlist, inspected the database. Outcome 1 entry in files but _DATA detail has .m3u appended and in the Music folder there is an empty .m3u file. If you got this to work, perhaps you have a worked example? – Theo Apr 15 '21 at 15:14
1

I came across the same issue. As the MediaProvider changes to Google's MediaProvider, the Scoped Storage feature is activated. When you try to modify a playlist file, but it's not created by your app (or it did be created by your app, but after OTA to new Android version, which changes to use Google's MediaProvider, it scans your playlist file and put a record to its database, but leaves the owner_package_name colume empty, it's a new colume, the old MediaProvider database has no owner_package_name colume, so no one could tell this playlist file was created by you), you will get a SecurityException says you have no access to this file.

You can check if the playlist file was owned by your app before doing the 'insert' operation:

Uri uri = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
String[] projection = new String[] {
        MediaStore.Audio.Playlists._ID,
        MediaStore.Audio.Playlists.NAME,
        MediaStore.Audio.Playlists.OWNER_PACKAGE_NAME,
};
String where = MediaStore.Audio.Playlists._ID + "=?";
String[] args = new String[] {playlistFileId};
Cursor cursor = resolver.query(uri, projection, where, args, null);
if (cursor != null) {
    cursor.moveToFirst();
    if (!cursor.isAfterLast()) {
        String ownerPkg = cursor.getString(
                cursor.getColumnIndex(MediaStore.Audio.Playlists.OWNER_PACKAGE_NAME));
        // print ownerPkg here
    }
}

If the owner package name of this playlist file is empty or other app's package name, that you probably have no access to write this playlist file due to the scoped storage feature limit.

According to this document, we can consider using MediaStore.createWriteRequest() method to prompt user to grant write permission to playlist file for our own app, but this request only available to certain kind of files, like images, audios, videos etc, but not for some other kinds like playlist files which ends in .m3u suffix.

Also, according to this, when you try to operate some image or audio files that's not created by your app in public storage, you will get a RecoverableSecurityException and you can use this exception to prompt user to get user consent to modify the file, but for playlist kind files, you will just get SecurityException instead of RecoverableSecurityException.

So the result is, you may never be able to access to that playlist file again, you can not modify it, and you can not delete it too. My solution is just create a new playlist file, so it's owned by my app, now I finally have full access to it. You may need to migrate your old playlist data to the new one.

Chipunpun
  • 40
  • 6
  • "My solution is just create a new playlist file, so it's owned by my app". All playlists are created by may app but inserting into this playlist fails. I can create/delete them without any issue, inserting is the problem – Theo Oct 22 '21 at 15:21
1

AND FINALLY I FIND THIS MediaStore.Audio.Playlists This class was deprecated in API level 31. Android playlists are now deprecated. We (Google) will keep the current functionality for compatibility resons, but we will no longer take feature request. We do not advise adding new usages of Android Playlists. M3U files can be used as an alternative.

In conclusion, no longer a relevant post

Theo
  • 2,012
  • 1
  • 16
  • 29
0

I have implemented the SAF so do not use scopedStorage and have access once the user accepts. The fact that I can insert new playlist entries clearly shows access to MediaStore, I can also delete these. However trying to add tracks to these playlists does not work for api29. Inserting/deleting a new playlist does not involve any files located on internal or external sdcards as it is simply adding values.

the permissions for both internal and external sdcard:

2020-07-12 14:39:04.435 11858-11858/com.flyingdutchman.newplaylistmanager E/onCreate:: uriPermission: UriPermission {uri=content://com.android.externalstorage.documents/tree/17F5-240A%3A, modeFlags=3, persistedTime=1594551961263}
2020-07-12 14:39:04.435 11858-11858/com.flyingdutchman.newplaylistmanager E/onCreate:: uriPermission: UriPermission {uri=content://com.android.externalstorage.documents/tree/primary%3A, modeFlags=3, persistedTime=1594551926876}

The question now becomes

How do I ensure saf permissions are recognised by the resolver.insert method when inserting/modify tracks into the Media database

Theo
  • 2,012
  • 1
  • 16
  • 29
  • I also have SAF implemented but when i call update on a contentresolver eg. `contentResolver.update(mediaFileUri, values, null, null);` it still throws a `RecoverableSecurityException` did you find a way that we don't need to catch that exception and request permission for each file individually? That's what i'm doing right now but i really hate it and it's a real hassle. – Vince VD May 27 '20 at 02:15
  • @Vince VD, not any further forward. It is unbelievable that there appears to be nobody creating playlists in android 10. In the meantime android11 is around the corner and more things get deprecated. Tempting just to give up – Theo May 27 '20 at 16:59
  • Creating playlists seem to work on alot of mp3 players apps on the Play Store but what i noticed is that they don't insert a new playlist in the MediaStore. So i think they manage their own database with playlists and it looks like its our only option right now. – Vince VD May 31 '20 at 05:30
0

Update May 2020

Stepping through the resolver code with debug F7

Scenario 1 results in permission error (incorrect MediaStore.VOLUME_EXTERNAL).

 playlist_uri = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL);
 playlist_members_uri  = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
                    .buildUpon()
                    .appendEncodedPath(Long.toString(playlist_id))
                    .appendEncodedPath("members")
                    .build();

acquireProvider(mContext, auth); = media

Uri createdRow = provider.insert(mPackageName, mAttributionTag, url, values, extras); = null

mPackageName=com.flyingdutchman.newplaylistmanager

mAttributionTag=null

values[0] = 206 values[1]=69 values[2]=1

extras=null

DatabaseUtils.java

   public static final void readExceptionFromParcel(Parcel reply) {
        int code = reply.readExceptionCode();
        if (code == 0) return;
        String msg = reply.readString();
        DatabaseUtils.readExceptionFromParcel(reply, msg, code);
    }

msg = com.flyingdutchman.newplaylistmanager has no access to content://media/external_primary/audio/playlists/206

Scenario 2 results in NO permission error BUT no tracks added to audio_playlists_map table.

 playlist_uri = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
url=content://media/external_primary/audio/playlists/206/members
Theo
  • 2,012
  • 1
  • 16
  • 29