Android-使用Google Scoped Storage API以本机C/C ++代码访问文件 [英] Android - accessing files in native C/C++ code with Google Scoped Storage API

查看:215
本文介绍了Android-使用Google Scoped Storage API以本机C/C ++代码访问文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要在本机C/C ++代码中的Android应用中按文件名打开文件.本机代码是我不希望修改的第三方库,但是它们通常需要文件名作为读取/写入文件的参数.有了Google的范围存储" API并在Android 10或更高版本中禁用了对文件的本地访问,这是一个真正的问题.

I need to open files by file name in Android apps within native C/C++ code. The native code are 3rd party libraries that I would prefer not to modify, but they often require file name as an argument to read/write files. With the Google's "scoped storage" API and disabling native access to files in Android 10 or later, it's a real problem.

一种众所周知的解决方案是获取文件描述符并使用"proc/self/fd/FD_NUMER"技巧,例如:

One well known solution is to get a file descriptor and use "proc/self/fd/FD_NUMER" trick, like:

       ParcelFileDescriptor mParcelFileDescriptor = null;

       String getFileNameThatICanUseInNativeCode(Context context, DocumentFile doc) { 
           try {
                Uri uri = doc.getUri();
                mParcelFileDescriptor =
                        context.getContentResolver().openFileDescriptor(uri, "r");
                if (mParcelFileDescriptor != null) {
                    int fd = mParcelFileDescriptor.getFd();
                    return "/proc/self/fd/" + fd;
                }
            }
            catch (FileNotFoundException fne) {
                return "";
            }
        }

        // Don't forget to close mParcelFileDescriptor when done!

将其传递给本机C/C ++代码有效,但是仅当文件位于电话主存储器中时.如果用户尝试打开插入到手机插槽中的外部SD卡上的文件,则该文件不起作用-这种方式打开的文件没有读取权限.我只能获取文件描述符int编号并使用fdopen(fd).但这将需要修改第三方库(开放源代码或许可的)的源代码,并且每当更新这些库的原始源时都会感到头疼.

Passing this to native C/C++ code works, but only if the file is in phone main storage. If the user tries to open a file that is on external SD card inserted into the phone slot, it does not work - there is no read permission for the file opened this way. I can only grab the file descriptor int number and use fdopen(fd). But this will require modifying the source code of 3rd party libraries (open source or licensed), and a big headache, whenever the original source of these libraries is updated.

有没有更好的解决方案来解决这个问题?不,我不想听到添加

Is there any better solution to that problem? And no, I don't want to hear the solution with adding

android:requestLegacyExternalStorage="true"

至AndroidManifest.xml应用程序部分-Google威胁要在2020年的下一版Android中禁用该功能,因此需要永久解决方案.另一个简单但愚蠢的解决方案是将用户尝试打开的整个文件(可能很大)复制到私有应用程序目录中.愚蠢而无用的...

to AndroidManifest.xml application section - Google threatens to disable that in the next version of Android in 2020, so a permanent solution is needed. Another easy but dumb solution is copying the entire (maybe huge) file that the user tries to open into private app directory. Dumb and useless...

推荐答案

更新2020年5月19日:刚刚发现:READ_EXTERNAL_STORAGE权限允许您在Android 11上运行并定位时读取文件,但不列出目录内容API 30(或更高版本).我将其提交为错误,并收到此功能按预期运行"答复( https://issuetracker.google.com/issues/156660903 ).如果有人在意,请在此处以及在其调查"中进行评论:https://google.qualtrics.com/jfe/form/SV_9HOzzyeCIEw0ij3?Source=scoped-storage 我不知道如何在所有这些限制下继续在Android上开发应用.

Update May 19, 2020: just discovered: the READ_EXTERNAL_STORAGE permission lets you read files, but not list directory contents, when running on Android 11 and targeting API 30 (or higher in the future). I submitted it as a bug and just got "This is working as intended" reply (https://issuetracker.google.com/issues/156660903). If anyone cares, please comment there, and also at their "survey": https://google.qualtrics.com/jfe/form/SV_9HOzzyeCIEw0ij3?Source=scoped-storage I have no idea how to proceed developing apps on Android with all these limitations.

2020年5月17日更新:Google最终承认并允许在Android 11及更高版本中使用READ_EXTERNAL_STORAGE权限.在花了几个月的时间将我的应用程序转换为Storage Access Framework(SAF)之后,我现在需要一两周的时间才能将其至少转换为读取文件部分,然后再转换为常规文件访问...谢谢Google!讽刺地感谢您所花费的时间和精力,并衷心感谢您至少部分地理解了我们的观点!

Update May 17 2020: Google has finally conceded and is permitting READ_EXTERNAL_STORAGE permission in Android 11 and beyond. After spending months in converting my apps to Storage Access Framework (SAF), I now take a week or two to convert it back, at least the reading files part, to regular file access... Thank you, Google! Thank you sarcastically, for the lost time and effort, and thank you sincerely, for at least partially understanding our point!

因此,请放心,不再需要下面列出的我以前的答案!

在Android"Scoped Storage" BS上度过了我美好的一天后,我找到了一种有效的解决方案,无需修改第三方本机库的源代码,只要该源代码具有源代码并且可以构建它们即可.

After wasting another good day of my life on the Android "Scoped Storage" B.S., I found a solution that works, without modifying the source of 3rd party native libraries, provided that one has the source of these libraries and can build them.

我的解决方案(非常不令人满意)是:在C/C ++编译命令中添加以下选项:

My (very unsatisfactory) solution is: add the following option to C/C++ compile command:

-include "[some/path/]idiocy_fopen_fd.h"

和idiocy_fopen_fd.h如下所示,您可以看到,对常规fopen()的每次调用都被idiocy_fopen_fd()代码替换,该代码检查文件名是否以"/proc/self/fd/"开头,如果是这样,则提取文件描述符号并调用fdopen()而不是fopen()...如果某人有更好的解决方案,最好是在您没有第三方库的源代码时也可以使用,请分享.

and the idiocy_fopen_fd.h is as follows, as you can see, every call to regular fopen() is replaced by the idiocy_fopen_fd() code, which checks if the file name starts with "/proc/self/fd/", and if so, extracts the file descriptor number and calls fdopen() instead of fopen()... If someone has a better solution, preferably which would work also when you don't have the source code of the 3rd party libs, please share.

#ifndef fopen_fd

#include <stdio.h>
#include <string.h>
#include <unistd.h> // for dup()

#ifdef __cplusplus
extern "C" {
#endif

inline FILE* idiocy_fopen_fd(const char* fname, const char * mode) {
  if (strstr(fname, "/proc/self/fd/") == fname) {
    int fd = atoi(fname + 14);
    if (fd != 0) {
      // Why dup(fd) below: if we called fdopen() on the
      // original fd value, and the native code closes
      // and tries re-open that file, the second fdopen(fd)
      // would fail, return NULL - after closing the
      // original fd received from Android, it's no longer valid.
      FILE *fp = fdopen(dup(fd), mode);
      // Why rewind(fp): if the native code closes and 
      // opens again the file, the file read/write position
      // would not change, because with dup(fd) it's still
      // the same file...
      rewind(fp);
      return fp;
    }
  }
  return fopen(fname, mode);
}
// Note that the above leaves the original file descriptor
// opened when finished - close parcelFileDescriptor in
// Java/Kotlin when your native code returns!

#ifdef __cplusplus
}
#endif

#define fopen idiocy_fopen_fd

#endif

这篇关于Android-使用Google Scoped Storage API以本机C/C ++代码访问文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆