Reputation: 2042
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
Upvotes: 18
Views: 11640
Reputation: 2042
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
Upvotes: 1
Reputation: 50
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.
Upvotes: 1
Reputation: 2042
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
Upvotes: 0
Reputation: 2042
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
Upvotes: 1
Reputation: 11
_id | _display_name | volume_name |
---|---|---|
308 | New playlist.m3u | external_primary |
The playlist's volume name is "external_primary".
2.
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.
Upvotes: 1
Reputation: 2042
in my further research for the answer, I came across this;
All about the media database (Mediastore) with android 11
Upvotes: 1
Reputation: 2042
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
Upvotes: 0
Reputation: 5037
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).
Upvotes: 8