我们正在编写一个针对 Android 进行交叉编译的 C++ 应用程序。该应用程序使用本机库(即gdal和Qt)来读取和写入文件。
这些文件通常相互引用(例如,xml 文件,引用其旁边的其他文件;或者 shapefile,其中多个文件具有共同的基本名称)。上述库需要能够通过路径 ( fopen(name)) 打开文件才能访问所有信息。
常见用例有:
由于 API 级别 30 ,Google PlayStore 限制文件访问,并且只允许在特定的应用程序特定目录中进行不受限制的直接文件访问。
在共享文件夹(例如下载文件夹、外部存储设备中的根文件夹等)中,只能通过直接文件访问来访问文件类型的子集。
共享存储文档中提到的访问媒体文件
如果您没有任何与存储相关的权限,则可以使用文件 API 访问应用程序特定目录中的文件以及归属于您的应用程序的媒体文件。
我们测试中的媒体文件包括图像、视频……但不包括此处所需的文件类型(.dbf、.shp、.qgs)。
还有存储访问框架,其中 DocumentProvider可用于流式传输单个文件和意图(如OPEN_DOCUMENT_TREE )的数据,但是这些将通过与本机调用不兼容的 URI 生成访问fopen(name)。
我们发现能够按名称打开文件(通过本机实验室)的唯一选项是使用所有文件权限 ( MANAGE_EXTERNAL_STORAGE)。这效果很好,但需要列入白名单才能在 Google Play 商店上发布。
谷歌支持建议使用系统文件选择器(有时将建议扩展到“或其他隐私友好的方法”)。
问:如果您的库之前可以使用路径,那么它们仍然可以。问题从哪里开始呢?
答:在 Android 11 上,未经许可MANAGE_EXTERNAL_STORAGE,我们在多次测试中看到,通过路径字符串直接打开共享位置中的非媒体文件不再起作用。文件打开失败。我们还注意到,执行目录列表(使用 Qt API)仅显示媒体文件扩展名,跳过所有其他类型。
如何在没有所有文件权限 ( MANAGE_EXTERNAL_STORAGE) 的情况下通过本机库共享位置中的文件路径(字符串)打开文件?
我有一个用 Qt 为 Android 构建的应用程序。该应用程序附带自定义构建的 openssl 版本。这适用于大多数设备,但是,在一些设备上,一旦 https 请求完成,它就会崩溃。
一个例子是 SAMSUNG-SM-G930AZ,API 级别 26 (Android 8.0)。
崩溃时它会产生以下跟踪。
backtrace:
#00 pc 00000000000667c4 /system/lib64/libc.so (strcasecmp+8)
#01 pc 000000000023ddd0 /system/lib64/libandroid_runtime.so (EVP_get_cipherbyname+24)
#02 pc 000000000003a5f0 /data/app/ch.opengis.qfield_beta-ojJ20GzjLOx-tvpq9AD27A==/lib/arm64/libssl_1_1.so (offset 0x29000)
Run Code Online (Sandbox Code Playgroud)
我libandroid_runtime.so从这个设备下载了文件,其中列出了提到的符号
readelf -Ws libandroid_runtime.so | grep EVP_get_cipherbyname
3593: 000000000023dd78 336 FUNC GLOBAL DEFAULT 12 EVP_get_cipherbyname
Run Code Online (Sandbox Code Playgroud)
在另一个(不崩溃 SM-T580,Android 8.1)上,情况并非如此
readelf -Ws /tmp/libandroid_runtime.so | grep EVP_get_cipherbyname
Run Code Online (Sandbox Code Playgroud)
该符号也libcrypto_1_1.so随应用程序一起提供。我认为这次碰撞是导致崩溃的原因。
在Android 文档中,Android 7 完成了以下 NDK 更改:
为了减少此限制可能对当前发布的应用程序产生的影响,
libandroid_runtime.so针对 API 级别的应用程序,可以在 Android 7.0(API 级别 24)上临时访问一组具有重要用途的库(例如,[...]) 23 …