【Android N兼容问题】Android N上系统预置应用调用第三方库初始化失败

本文主要是介绍【Android N兼容问题】Android N上系统预置应用调用第三方库初始化失败,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

【Android N兼容问题】Android N上系统预置应用调用第三方库初始化失败

一、问题描述

  开发系统ROM需要预置一款内部应用,预置位置在”system/priv-app下”,该应用使用到了百度地图的so库; 在调用到该so库的地方,报初始化失败,查看log发现以下出错log:

05-22 10:02:17.963 11659 11659 E linker  : library "/data/user/0/com.xxx.xxx.voc/lib/libgnustl_shared.so" ("/system/priv-app/xxx/lib/arm/libgnustl_shared.so") needed or dlopened by "/system/lib/libnativeloader.so" 
is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="", permitted_paths="/data:/mnt/expand"]
...
05-22 10:02:17.965 11659 11659 W System.err: Caused by: java.lang.UnsatisfiedLinkError: dlopen failed: library "/data/user/0/com.xxx.xxx.voc/lib/libgnustl_shared.so" 
needed or dlopened by "/system/lib/libnativeloader.so" is not accessible for the namespace "classloader-namespace"
05-22 10:02:17.965 11659 11659 W System.err:    at java.lang.Runtime.load0(Runtime.java:908)
05-22 10:02:17.965 11659 11659 W System.err:    at java.lang.System.load(System.java:1505)
05-22 10:02:17.965 11659 11659 W System.err:    at com.baidu.navisdk.adapter.impl.BaiduNaviManager.loadNativeLibrary(BaiduNaviManager.java:311)
05-22 10:02:17.965 11659 11659 W System.err:    at com.baidu.navisdk.adapter.impl.BaiduNaviManager.loadBaiduNaviNativeLibrary(BaiduNaviManager.java:294)
05-22 10:02:17.965 11659 11659 W System.err:    at com.baidu.navisdk.adapter.impl.BaiduNaviManager.init(BaiduNaviManager.java:693)
05-22 10:02:17.965 11659 11659 W System.err:    at com.baidu.navisdk.adapter.impl.BaiduNaviSDKStub.init(BaiduNaviSDKStub.java:399)
05-22 10:02:17.965 11659 11659 W System.err:    ... 17 more

  log显示应该是百度sdk的so库is not accessible for the namespace,因此load so库时失败,导致最终百度地图sdk初始化失败;
  该应用开发人员告知使用adb install -r 方式可以成功初始化,但是push的方式无法成功初始化,同样编到版本里的该应用同样无法初始化成功。

二、问题分析:

1.找到打印出错的代码位置

  打印这句异常log的地方,是在 /bionic/linker/linker.cpp中的static bool load_library()函数中:

/bionic/linker/linker.cpp
static bool load_library(android_namespace_t* ns,LoadTask* task,LoadTaskList* load_tasks,int rtld_flags,const std::string& realpath) { 
...if (!ns->is_accessible(realpath)) {// TODO(dimitry): workaround for http://b/26394120 - the grey-listconst soinfo* needed_by = task->is_dt_needed() ? task->get_needed_by() : nullptr;if (is_greylisted(name, needed_by)) {// print warning only if needed by non-system libraryif (needed_by == nullptr || !is_system_library(needed_by->get_realpath())) {const soinfo* needed_or_dlopened_by = task->get_needed_by();const char* sopath = needed_or_dlopened_by == nullptr ? "(unknown)" :needed_or_dlopened_by->get_realpath();DL_WARN("library \"%s\" (\"%s\") needed or dlopened by \"%s\" is not accessible for the namespace \"%s\""" - the access is temporarily granted as a workaround for http://b/26394120, note that the access"" will be removed in future releases of Android.",name, realpath.c_str(), sopath, ns->get_name());add_dlwarning(sopath, "unauthorized access to",  name);}} else {// do not load libraries if they are not accessible for the specified namespace.const char* needed_or_dlopened_by = task->get_needed_by() == nullptr ?"(unknown)" :task->get_needed_by()->get_realpath();DL_ERR("library \"%s\" needed or dlopened by \"%s\" is not accessible for the namespace \"%s\"",name, needed_or_dlopened_by, ns->get_name());PRINT("library \"%s\" (\"%s\") needed or dlopened by \"%s\" is not accessible for the"" namespace: [name=\"%s\", ld_library_paths=\"%s\", default_library_paths=\"%s\","" permitted_paths=\"%s\"]",name, realpath.c_str(),needed_or_dlopened_by,ns->get_name(),android::base::Join(ns->get_ld_library_paths(), ':').c_str(),android::base::Join(ns->get_default_library_paths(), ':').c_str(),android::base::Join(ns->get_permitted_paths(), ':').c_str());return false;}}...return true;
}

我们注意到注释:

 // TODO(dimitry): workaround for http://b/26394120 - the grey-list// print warning only if needed by non-system library// do not load libraries if they are not accessible for the specified namespace.

这里其实是Android N的新的特性。

2. Android N 限制应用使用非公共NDK API

  从Android N开始,本地库只能引用公共的NDK API,系统将阻止应用动态连接非NDK平台的so库。Android 7.0 行为变更 中指出如果您的应用使用动态链接到私有平台 API 的第三方库,您可能也会看到上述错误 logcat 输出。
  谷歌也列了一个灰名单(grey list),这个灰名单上的so库可以被第三方apk调用。这个灰名单是Google play上Top 100的apk使用频率最高的so库。但是谷歌也声明了,这个灰名单措施是暂时性的,以后会逐步去除。

/bionic/linker/linker.cpp
// TODO(dimitry): The grey-list is a workaround for http://b/26394120 ---
// gradually remove libraries from this list until it is gone.
static bool is_greylisted(const char* name, const soinfo* needed_by) {static const char* const kLibraryGreyList[] = {"libandroid_runtime.so","libbinder.so","libcrypto.so","libcutils.so","libexpat.so","libgui.so","libmedia.so","libnativehelper.so","libskia.so","libssl.so","libstagefright.so","libsqlite.so","libui.so","libutils.so","libvorbisidec.so",nullptr};

  我们尝试将库 “/data/user/0/com.xxx.xxx.voc/lib/libgnustl_shared.so”,加到这个名单中,然后单编linker,push到system/bin下,添加可执行权限,然后发现,在load这个库时通过了,但该应用引用的其他第三方库还是报了上面log的异常,我们添加该应用所有的第三方库这里就不会报错了,但显然这不是一个好的解决方案。

3. 比较adb install -r 和adb push区别

  在linker.cpp的load_library()函数一开始的地方加上log

static bool load_library(android_namespace_t* ns,LoadTask* task,LoadTaskList* load_tasks,int rtld_flags,const std::string& realpath) {off64_t file_offset = task->get_file_offset();const char* name = task->get_name();const android_dlextinfo* extinfo = task->get_extinfo();PRINT("mydebug library \"%s\" (\"%s\") ,permitted_paths=\"%s\"]",name, realpath.c_str(),android::base::Join(ns->get_permitted_paths(), ':').c_str());...}

  a.使用adb push至system/priv-app下时这里打的log是:

06-02 16:28:19.299  8809  8809 E linker  : mydebug library "/data/user/0/com.xxx.xxx.voc/lib/libapp_BaiduVIlib.so" ("/system/priv-app/xxx/lib/arm/libapp_BaiduVIlib.so") , permitted_paths="/data:/mnt/expand"]

  b.使用adb install -r的方式发现初始化成功这里打的log是:

06-02 16:29:42.346  9585  9585 E linker  : mydebug library "/data/user/0/com.xxx.xxx.voc/lib/libapp_BaiduVIlib.so" ("/data/app/com.xxx.xxx.voc-1/lib/arm/libapp_BaiduVIlib.so") ,permitted_paths="/data:/mnt/expand"]

  可以看到adb install -r的方式是将apk安装到了data/app下,而后面的log显示permitted_paths是包含/data路径下的因此使用adb install -r的方式是不会报错的;,
  在源码里搜”/data:/mnt/expand”意外的发现:

//system/core/libnativeloader/native_loader.cpp
// (http://b/27588281) This is a workaround for apps using custom classloaders and calling
// System.load() with an absolute path which is outside of the classloader library search path.
// This list includes all directories app is allowed to access this way.
static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand";

  意思就是使用System.load() 一个在classloader的库之外的绝对路径下的库,这些路径之下是可以获取的。使用adb install 或者点击安装包安装都会安装在data/app下,而kWhitelistedDirectories 默认就有/data目录,因此正常安装的三方应用即使调用三方库也是ok的。
  我们尝试将system/priv-app加到这个permitted_paths中:

static constexpr const char* kWhitelistedDirectories = "/data:/mnt/expand:/system/priv-app";

  编system.img后验证ok:预置的apk调用百度地图sdk初始化成功。

三、总结:

  1.以上是做ROM开发时遇到的Android N上的库引用失败的案例,由于是手机ROM预置的应用,我们可以在平台的基础上修改已达到适配;
  2.但在网上搜了一番,很多出现类似的出错log是在三方应用加载系统私有库时出现:
    在AndroidN上当三方应用加载系统私有库的时候,系统会限制其加载,Android N加载系统私有库时出现is not accessible for the namespace错误和针对该错误的解决方法 这篇博客中指出,可以将需要的系统私有库pull出来,集成在apk项目中再使用System.loadLibrary()方法调用。
    当然,Android 7.0 行为变更#NDK 应用链接至平台库 这篇文章中详细介绍了三方应用调用私有库的规范方式,上面的方法及前面所提到的灰名单是Android N上的过度方式,做应用开发的话应当及时发现这个新改动并及时完善,以避免应用在新平台新机器中出现不好的用户体验。
  日后有新的使用经历再更新本篇博客。

参考博客:
Android 7.0 行为变更#NDK 应用链接至平台库:
https://developer.android.google.cn/about/versions/nougat/android-7.0-changes.html#perm
Android中adb push和adb install的使用区别:
http://www.cnblogs.com/permanent2012moira/p/4181317.html
Framework基础:AndroidN公共so库怎么定义:
http://www.qingpingshan.com/rjbc/az/247686.html
Android N加载系统私有库时出现is not accessible for the namespace错误和针对该错误的解决方法:
http://blog.csdn.net/wjskeepmaking/article/details/70153995

这篇关于【Android N兼容问题】Android N上系统预置应用调用第三方库初始化失败的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/825628

相关文章

Mac系统下卸载JAVA和JDK的步骤

《Mac系统下卸载JAVA和JDK的步骤》JDK是Java语言的软件开发工具包,它提供了开发和运行Java应用程序所需的工具、库和资源,:本文主要介绍Mac系统下卸载JAVA和JDK的相关资料,需... 目录1. 卸载系统自带的 Java 版本检查当前 Java 版本通过命令卸载系统 Java2. 卸载自定

CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比

《CSS中的Static、Relative、Absolute、Fixed、Sticky的应用与详细对比》CSS中的position属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布... css 中的 position 属性用于控制元素的定位方式,不同的定位方式会影响元素在页面中的布局和层叠关

SpringBoot3应用中集成和使用Spring Retry的实践记录

《SpringBoot3应用中集成和使用SpringRetry的实践记录》SpringRetry为SpringBoot3提供重试机制,支持注解和编程式两种方式,可配置重试策略与监听器,适用于临时性故... 目录1. 简介2. 环境准备3. 使用方式3.1 注解方式 基础使用自定义重试策略失败恢复机制注意事项

C++中RAII资源获取即初始化

《C++中RAII资源获取即初始化》RAII通过构造/析构自动管理资源生命周期,确保安全释放,本文就来介绍一下C++中的RAII技术及其应用,具有一定的参考价值,感兴趣的可以了解一下... 目录一、核心原理与机制二、标准库中的RAII实现三、自定义RAII类设计原则四、常见应用场景1. 内存管理2. 文件操

基于Python实现一个简单的题库与在线考试系统

《基于Python实现一个简单的题库与在线考试系统》在当今信息化教育时代,在线学习与考试系统已成为教育技术领域的重要组成部分,本文就来介绍一下如何使用Python和PyQt5框架开发一个名为白泽题库系... 目录概述功能特点界面展示系统架构设计类结构图Excel题库填写格式模板题库题目填写格式表核心数据结构

Linux系统中的firewall-offline-cmd详解(收藏版)

《Linux系统中的firewall-offline-cmd详解(收藏版)》firewall-offline-cmd是firewalld的一个命令行工具,专门设计用于在没有运行firewalld服务的... 目录主要用途基本语法选项1. 状态管理2. 区域管理3. 服务管理4. 端口管理5. ICMP 阻断

Java调用C#动态库的三种方法详解

《Java调用C#动态库的三种方法详解》在这个多语言编程的时代,Java和C#就像两位才华横溢的舞者,各自在不同的舞台上展现着独特的魅力,然而,当它们携手合作时,又会碰撞出怎样绚丽的火花呢?今天,我们... 目录方法1:C++/CLI搭建桥梁——Java ↔ C# 的“翻译官”步骤1:创建C#类库(.NET

Python使用Tkinter打造一个完整的桌面应用

《Python使用Tkinter打造一个完整的桌面应用》在Python生态中,Tkinter就像一把瑞士军刀,它没有花哨的特效,却能快速搭建出实用的图形界面,作为Python自带的标准库,无需安装即可... 目录一、界面搭建:像搭积木一样组合控件二、菜单系统:给应用装上“控制中枢”三、事件驱动:让界面“活”

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

Windows 系统下 Nginx 的配置步骤详解

《Windows系统下Nginx的配置步骤详解》Nginx是一款功能强大的软件,在互联网领域有广泛应用,简单来说,它就像一个聪明的交通指挥员,能让网站运行得更高效、更稳定,:本文主要介绍W... 目录一、为什么要用 Nginx二、Windows 系统下 Nginx 的配置步骤1. 下载 Nginx2. 解压