Android - accessing files in native C/C++ code with Google Scoped Storage API
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.
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!
So, my previous answer listed below won't be needed anymore, sigh of relief!
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.
My (very unsatisfactory) solution is: add the following option to C/C++ compile command:
-include "[some/path/]idiocy_fopen_fd.h"
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