在早期的Android系统版本中,正常Android在预置本地共享库时,只需要把相应的库放入/system/lib/或者/system/lib64目录就可以了。
但在Android N(7.0)版本之后,推出了针对原生库的命名空间,旨在限制内部 API 的可见性,并解决应用意外地使用平台库(而非它们自己的库)这一问题。
这项更改会将系统库与应用库分离开来,从而很大程度上避免意外使用内部系统库(反之亦然)。
新版预制库feature设计架构:
添加其他原生库注意事项
除了标准的公共原生库之外,芯片供应商(从 Android 7.0 起)和设备制造商(从 Android 9 起)还可以选择提供可供应用访问的其他原生库,方法是将它们放在相应的库文件夹中,并在 .txt 文件中明确列出它们。
库文件夹是:
/vendor/lib(对于芯片供应商的 32 位库)和 /vendor/lib64(对于芯片供应商的 64 位库)
/system/lib(对于设备制造商的 32 位库)和 /system/lib64(对于设备制造商的 64 位库)
修改对应的.txt 文件是:
/vendor/etc/public.libraries.txt 或 /system/etc/public.libraries.txt(对于芯片供应商的库)
/system/etc/public.libraries-COMPANYNAME.txt(对于设备制造商的库),其中 COMPANYNAME 指的是制造商的名称(例如 aweson company等)。如果某些库来自外部解决方案提供商,则可以在设备中包含多个此类 .txt 文件。
必须将 system 分区内由设备制造商公开提供的原生库命名为 lib*companyName.so,例如 libFoo.awesome.company.so。换句话说,没有公司名称后缀的libFoo.so不得公开。库文件名中的 COMPANYNAME 必须与列出库名称的 txt 文件名称中的 COMPANYNAME 匹配。
举个遇到具体问题的例子:
应用在修改系统应用camera2时,需要新加入一个libHexin.so库,我们默认帮他预制到system/lib中,应用通过System.loadLibrary 方法加载这个库,一切看起来很美好。。
但是camera2应用在运行时,却弹出强制关闭,抓取log提示如下:
这个问题就是典型的内部库访问外部库命名空间被限制的问题,所以需要在对应的.txt文件中加入这个libHexin.so库就可以解决问题:
我们再来研究一下这个feature的具体代码实现:
/system/core/libnativeloader/native_loader.cpp
static constexpr const char* kPublicNativeLibrariesSystemConfigPathFromRoot =
"/etc/public.libraries.txt";
static constexpr const char* kPublicNativeLibrariesVendorConfig =
"/vendor/etc/public.libraries.txt";
static constexpr const char* kLlndkNativeLibrariesSystemConfigPathFromRoot =
"/etc/llndk.libraries.txt";
static constexpr const char* kVndkspNativeLibrariesSystemConfigPathFromRoot =
"/etc/vndksp.libraries.txt";
void Initialize() {
..................
std::vector<std::string> sonames;
system_public_libraries_ = base::Join(sonames, ':');
sonames.clear();
ReadConfig(kLlndkNativeLibrariesSystemConfigPathFromRoot, &sonames);
system_llndk_libraries_ = base::Join(sonames, ':');
sonames.clear();
ReadConfig(kVndkspNativeLibrariesSystemConfigPathFromRoot, &sonames);
system_vndksp_libraries_ = base::Join(sonames, ':');
sonames.clear();
// This file is optional, quietly ignore if the file does not exist.
ReadConfig(kPublicNativeLibrariesVendorConfig, &sonames);
vendor_public_libraries_ = base::Join(sonames, ':');
}
在LibraryNamespaces类的Initialize()会读取这个文件,将so库设置为公共so库,所谓公共so库,就是这个so库其他人都能用。
这个方法时什么时候调用的呢?大概看下具体实现流程
首先在创建一个虚拟机的时候,初始化NativeLoader,这个NativeLoader,顾名思义,就是用来装载so库的。
java_vm_ext.cc
android::InitializeNativeLoader();
然后进入刚才的native_loader.cpp 代码中 ,这样就回到了刚才公共库申明查询.txt文件的地方了。
然后进入刚才的native_loader.cpp 代码中 ,这样就回到了刚才公共库申明查询.txt文件的地方了。
void InitializeNativeLoader() {
#if defined(__ANDROID__)
std::lock_guard<std::mutex> guard(g_namespaces_mutex);
g_namespaces->Initialize();
#endif