【问题标题】:API 29 Mediastore AccessAPI 29 媒体存储访问
【发布时间】:2019-12-15 07:49:11
【问题描述】:

我的应用在 android mediastore 中创建播放列表。包括 28 在内的 api 一切都很好,但是 api 29 似乎需要额外的权限。 插入新的播放列表名称和 ID 可以正常工作。插入track id和播放顺序时,会抛出访问权限异常。 在验证 Uri 时,我发现当 resolver.insert for API 29 时出现异常错误:

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

代码:

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();
}

奇怪的是,虽然将新的播放列表插入 Mediastore 可以,但添加曲目(track_id,播放顺序)会导致访问权限错误

如何解决 API 29 的这个异常错误?

2021 年 2 月更新: 向前迈出了一小步,我很确定我需要为原始 uri 获取 Documenturi,但仍然给我访问错误。所以问题不在于访问轨道,而在于 uri 本身。

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

【问题讨论】:

  • 不要只使用外部硬编码,使用外部主卷。
  • developer.android.com/training/data-storage/shared/media 尝试使用 MediaStore.VOLUME_EXTERNAL_PRIMARY(在 API
  • @Khoa Nguyễn 试过这个没有用
  • 我也有同样的问题。我可以从播放列表中删除曲目,但是在添加时,我在 API 29 上遇到了同样的异常。您找到解决方案了吗?我很乐意为此提供帮助。
  • @EvanBlack 嗨。目前,我使用基于文件的 m3u 播放列表进行了解决。系统扫描这些并创建播放列表。市长的缺点是每次重新创建播放列表时 id 都会发生变化。因此,某些使用播放列表的应用程序每次更改时都需要更新。

标签: android mediastore android-10.0


【解决方案1】:

我认为这是一个 Android 10 错误,因此我在此处提交了一份报告:https://issuetracker.google.com/issues/147619577(如果您感兴趣,请提供仿真器测试用例的说明以重现它)。请考虑给它加星标,让 Android 团队知道它对您有影响。

据我所知,它只会影响“外部”存储上的文件,例如安装在 /storage/XXXX-XXXX 上的 sdcard

与此同时,我的一些用户能够成功应用的唯一修复是将他们的音乐文件移动到内部存储(重新启动并等待媒体扫描完成以确保 MediaStore 是最新的-日期)。

【讨论】:

  • 谢谢,我会试试内存中的音乐
  • android 10 引入了一个新的存储概念:Scoped storage,如果您对 Scoped Storage 不满意,请将此标志添加到您的清单文件中(但不推荐,因为它将在 Android 的下一个版本中强制执行):
  • @toumir 该错误还会影响针对 API 28 或使用 requestLegacyExternalStorage="true" 的应用,因此这无济于事。
  • @now 不是错误,但此行为会影响针对 API 29 或 + 的应用,API 28 及以下版本对我来说正常工作。
  • 我很想看看。你能分享一个可行的项目吗?
【解决方案2】:

在我对答案的进一步研究中,我遇到了这个;

All about the media database (Mediastore) with android 11

【讨论】:

    【解决方案3】:
    1. 使用 uri“MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI”创建播放列表,播放列表在 external.db 中的日期行是:
    _id _display_name volume_name
    308 New playlist.m3u external_primary

    播放列表的卷名是“external_primary”。

    2.

    • 音乐文件在闪存卡下
    • external.db 中音乐文件的 id 为 278
    • 闪存卡的卷名是“1EDD-DDE0”

    将此音乐文件添加到播放列表时,出现以下异常:
    异常消息:java.lang.SecurityException: has no access to content://media/external_primary/audio/media/278

    如果我用uri MediaStore.Audio.Playlists.getContentUri("1edd-dde0")创建播放列表,那么音乐可以成功添加到播放列表中。

    原因似乎是播放列表和要添加的音乐文件之间的卷名不匹配。只有当播放列表的卷名与音乐文件的卷名相同时,才能完成插入操作。

    【讨论】:

    • 我应用了这个建议,但是.... Uri uri_to_use = MediaStore.Audio.Playlists.getContentUri("0F16-2701"); -gives : content://media/0F16-2701/audio/playlists,并导致 java.lang.IllegalArgumentException: Volume 0F16-2701 not found
    • 我的意思是,您应该将播放列表与您要添加的歌曲放在同一音量上。在您的情况下,您应该在“0F16-2701”上创建播放列表,然后您可以将位于“0F16-2701”中的歌曲添加到播放列表中。
    • 感谢您的关注。我想我按照我的 uri_to_use 示例中的建议做了。我不明白为什么你需要卷名,因为 android 播放列表只是一个 entry_id 和文件表中 media_type=4 的名称的条目。我从一个干净的模拟器开始,创建了 1 个 android 播放列表,检查了数据库。文件中的结果 1 条目,但 _DATA 详细信息附加了 .m3u,并且在 Music 文件夹中有一个空的 .m3u 文件。如果你让这个工作,也许你有一个工作的例子?
    【解决方案4】:

    Android 11 更新。 值得注意的是,媒体数据库已从

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

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

    结构也发生了显着变化

    【讨论】:

    • 我尝试了所有方法,但在 API 28 之上仍然无法以这种方式工作。我放弃了,只是使用带有播放列表和歌曲表的 Room 创建了我自己的数据库。 ScopedStorage 毁了一切。
    【解决方案5】:

    我遇到了同样的问题。随着 MediaProvider 更改为 Google 的 MediaProvider,Scoped Storage 功能被激活。当您尝试修改播放列表文件,但它不是由您的应用程序创建的(或者它确实是由您的应用程序创建的,但是在 OTA 到新的 Android 版本后,它会更改为使用 Google 的 MediaProvider,它会扫描您的播放列表文件并记录到它的数据库,但将 owner_package_name 列留空,这是一个新的列,旧的 MediaProvider 数据库没有 owner_package_name 列,所以没有人知道这个播放列表文件是由你创建的),你会得到一个 SecurityException 说你无权访问此文件。

    您可以在执行“insert”操作之前检查播放列表文件是否归您的应用所有:

    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
        }
    }
    

    如果此播放列表文件的所有者包名称为空或其他应用程序的包名称,则可能由于范围存储功能限制,您无权写入此播放列表文件。

    根据document,我们可以考虑使用 MediaStore.createWriteRequest() 方法来提示用户为我们自己的应用程序授予对播放列表文件的写入权限,但这个请求仅适用于某些类型的文件,如图像、音频, 视频等,但不适用于其他类型,例如以 .m3u 后缀结尾的播放列表文件。

    另外,根据this,当你尝试在公共存储中操作一些不是你的应用程序创建的图像或音频文件时,你会得到一个RecoverableSecurityException,你可以使用这个异常来提示用户获取用户同意修改文件,但对于播放列表类文件,您只会得到SecurityException 而不是RecoverableSecurityException

    结果是,您可能永远无法再次访问该播放列表文件,您无法修改它,也无法删除它。我的解决方案只是创建一个新的播放列表文件,所以它归我的应用所有,现在我终于可以完全访问它了。您可能需要将旧的播放列表数据迁移到新的。

    【讨论】:

    • “我的解决方案是创建一个新的播放列表文件,所以它归我的应用所有”。所有播放列表均由 may 应用创建,但插入此播放列表失败。我可以毫无问题地创建/删除它们,插入是问题
    【解决方案6】:

    我已经实现了 SAF,所以不要使用 scopedStorage,一旦用户接受就可以访问。 我可以插入新的播放列表条目这一事实清楚地表明可以访问 MediaStore,我也可以删除这些条目。但是,尝试将曲目添加到这些播放列表不适用于 api29。插入/删除新播放列表不涉及位于内部或外部 sdcard 上的任何文件,因为它只是添加值。

    内部和外部 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}
    

    问题变成了

    在将曲目插入/修改到媒体数据库时,如何确保 saf 权限被 resolver.insert 方法识别

    【讨论】:

    • 我也实现了 SAF,但是当我在 contentresolver 上调用更新时,例如。 contentResolver.update(mediaFileUri, values, null, null); 它仍然抛出 RecoverableSecurityException 您是否找到了一种方法,我们不需要捕获该异常并单独为每个文件请求权限?这就是我现在正在做的事情,但我真的很讨厌它,这真的很麻烦。
    • @Vince VD,不再前进。令人难以置信的是,似乎没有人在 android 10 中创建播放列表。与此同时,android11 即将到来,更多的东西被弃用了。很想放弃
    • 创建播放列表似乎适用于 Play Store 上的许多 mp3 播放器应用程序,但我注意到他们没有在 MediaStore 中插入新的播放列表。所以我认为他们用播放列表管理自己的数据库,这看起来是我们现在唯一的选择。
    【解决方案7】:

    2020 年 5 月更新

    使用调试 F7 单步执行解析器代码

    场景 1 导致权限错误(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); =媒体

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

    mPackageName=com.flyingdutchman.newplaylistmanager

    mAttributionTag=null

    值[0] = 206 值[1]=69 值[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 无权访问 content://media/external_primary/audio/playlists/206

    场景 2 导致没有权限错误,但没有将曲目添加到 audio_playlists_map 表中。

     playlist_uri = MediaStore.Audio.Playlists.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
    url=content://media/external_primary/audio/playlists/206/members
    

    【讨论】:

      猜你喜欢
      • 2020-12-29
      • 2020-06-09
      • 2014-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多