Android - 使用 Google Scoped Storage API 访问本机 C/C++ 代码中的文件

Android - accessing files in native C/C++ code with Google Scoped Storage API(Android - 使用 Google Scoped Storage API 访问本机 C/C++ 代码中的文件)

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

问题描述

我需要在原生 C/C++ 代码中的 Android 应用程序中按文件名打开文件.本机代码是我不想修改的第 3 方库,但它们通常需要文件名作为读取/写入文件的参数.使用 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).但这将需要修改 3rd 方库(开源或许可)的源代码,而且每当这些库的原始源更新时,这是一个非常令人头疼的问题.

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 日更新:刚刚发现:在 Android 11 上运行并定位时,READ_EXTERNAL_STORAGE 权限允许您读取文件,但不能列出目录内容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 权限.在花费数月将我的应用程序转换为存储访问框架 (SAF) 之后,我现在需要一两个星期的时间将其转换回(至少是读取文件部分)到常规文件访问......谢谢你,谷歌!讽刺地感谢您浪费的时间和精力,并真诚地感谢您至少部分理解了我们的观点!

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 上浪费了我生命中美好的一天之后,我找到了一个可行的解决方案,无需修改 3rd 方本机库的源代码,前提是有人拥有这些库的源代码并且可以构建它们.

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()... 如果有人有更好的解决方案,最好在您没有第 3 方库的源代码时也可以使用,请分享.

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++ 代码中的文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:Android - 使用 Google Scoped Storage API 访问本机 C/C++ 代码中的文件

基础教程推荐