Theo
Theo

Reputation: 2042

API 29 Mediastore Access

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

Answers (8)

Theo
Theo

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

Chipunpun
Chipunpun

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

Theo
Theo

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

Theo
Theo

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

before android 10/11 and android 11

Upvotes: 1

wanted
wanted

Reputation: 11

  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.

Upvotes: 1

Theo
Theo

Reputation: 2042

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

All about the media database (Mediastore) with android 11

Upvotes: 1

Theo
Theo

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

now
now

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

Related Questions