[Android Framework]隐藏模块源码并封装为BootJar实战 您所在的位置:网站首页 apk源码修改隐藏 [Android Framework]隐藏模块源码并封装为BootJar实战

[Android Framework]隐藏模块源码并封装为BootJar实战

2023-07-07 02:41| 来源: 网络整理| 查看: 265

1.背景

客户想要拿到Framework的源码自己编译系统,但是我们在Framework中以源码形式集成了自己的拓展代码,故需要将我们的修改部分代码封装起来且以不暴露源码的Jar包形式重新集成到Framework中。

2.关键难点

(1)由于拓展部分的代码在设计之初并未打算封装,而是作为Framework的一部分源码直接编译,故与Framework的其他部分存在相互依赖的关系,这为后续封装代码创造了困难。 (2)由于需要的是Framework部分的源码,所以也不能直接将编译完成后的framework.jar提供给客户。 (2)本项目是基于Android 10的,故Framework中已从Android.mk换成了Android.bp,而网上只有Android.bp的基本使用语法,对于使用Android.bp封装源码的资料并不多。

3.必要知识准备

建议还是先看看必要知识,不要急于完成需求,不然可能会碰到很多问题,但是又不知道是哪里导致的,会浪费很多时间去瞎改。

(1)Android.bp语法以及与mk语法之间的对比

最好作为参照物之一的就是Framework中service/core部分的bp文件

java_library_static { //编译一个静态库 name: "services.core.unboosted",//模块名称, 类似Android.mk中的LOCAL_MODULE aidl: {//aidl文件路径 include_dirs: [//指定的文件查找路径 "frameworks/base/cmds/idmap2/idmap2d/aidl", ], }, srcs: [//源码 "java/**/*.java", ":dumpstate_aidl", ], libs: [//动态库 "services.net", ], required: [//前提条件 "gps_debug.conf", ], static_libs: [//静态库 "time_zone_distro", ], } java_genrule {//类似于file_group的用法 name: "services.core.priorityboosted", srcs: [":services.core.unboosted"], tools: ["lockedregioncodeinjection"], cmd: "$(location lockedregioncodeinjection) " + " --targets \"Lcom/android/server/am/ActivityManagerService;,Lcom/android/server/wm/WindowManagerGlobalLock;\" " + " --pre \"com/android/server/am/ActivityManagerService.boostPriorityForLockedSection,com/android/server/wm/WindowManagerService.boostPriorityForLockedSection\" " + " --post \"com/android/server/am/ActivityManagerService.resetPriorityAfterLockedSection,com/android/server/wm/WindowManagerService.resetPriorityAfterLockedSection\" " + " -o $(out) " + " -i $(in)", out: ["services.core.priorityboosted.jar"], } java_library {//编译生成一个库 name: "services.core", static_libs: ["services.core.priorityboosted"], } prebuilt_etc {//预编译配置文件,权限文件也通过该方式 name: "gps_debug.conf", src: "java/com/android/server/location/gps_debug.conf", }

针对该场景和需求所需知道的MK与BP文件的部分对照内容,详情可直接阅读源码:

build/soong/androidmk/cmd/androidmk/android.go

"BUILD_JAVA_LIBRARY": "java_library", "BUILD_STATIC_JAVA_LIBRARY": "java_library_static", "BUILD_HOST_JAVA_LIBRARY": "java_library_host", "BUILD_HOST_DALVIK_JAVA_LIBRARY": "java_library_host_dalvik", "BUILD_PACKAGE": "android_app", "SHARED_LIBRARIES": "cc_prebuilt_library_shared", "STATIC_LIBRARIES": "cc_prebuilt_library_static", "EXECUTABLES": "cc_prebuilt_binary", "JAVA_LIBRARIES": "prebuilt_java_library",

常见属性:

platform_apis : 用 sdk 的 hide 的 api 來编译 certificate : 指定用的是什么签名,如上用的是 platform 签名。 jni_libs : 依赖使用的 JNI 库 libs : 工程中的 libs 库 static_libs : 静态库,其中 nearme_nfc 为下方定义的:java_import optimize : 压缩配置,enabled 是否开启,obfuscate 是否开启混淆,proguard_flags_files 混淆规则配置文件 (2)库文件在AOSP源码环境下的用法

首先,我们要知道一件事,在Android.bp中为什么会有libs和static_libs两种库的引用,原因在于使用libs时,只是在编译该模块时去依赖所添加的libs,而static_libs则是会将所依赖的代码一起编译在模块中。

因此,如果仅仅只是想要模块编译通过,又能确保在系统运行时系统中是存在所依赖的那部分代码的话,只需要使用libs即可。但是在当前这个需求中,不仅需要通过编译,又要将其他模块所依赖的该模块封装为库,故,需要将该模块编译出两种库来。

Jar A:仅包含接口文件,即类和方法接口的class文件,但是在方法中没有函数实现,目的是为了使所依赖该模块的模块通过编译。 Jar B:不包含接口文件,但是包含该模块的源代码的dex文件,用于系统加载该模块的运行代码。

关于java和class和dex的关系,可以看看下面这个图。

在这里插入图片描述

(3)BootJar添加方法

Step1:定义需要做成BootJar的模块Rabbit Android.mk或者Android.bp均可,但是最好用Android.bp,因为以后都会是BP。

Step2:在device中用于指定编译哪些模块的mk文件(各公司各项目可能不一样)中添加Rabbit 以及权限文件

Step3:在mk文件中定义PRODUCT_BOOT_JARS的地方添加Rabbit,并将Rabbit加入白名单。 白名单文件路径:

build/make/core/tasks/check_boot_jars/package_whitelist.txt

Step4:在adb shell中通过指令adb shell $BOOTCLASSPATH验证BOOTCLASSPATH是否已包含所添加的Rabbit

4.实际操作

其实实际上就第一步复杂点,其他的都是照葫芦画瓢网上也有一些相关的资料可以参考。

Step 1 首先,我们需要取得仅包含接口文件的Jar A以及仅包含dex的Jar B。 这里就不贴代码了,一般来说能碰到这个需求说明已经有了源码,只需要修改一下编译文件,生成一个java_library,并指定installable: true,即可,关于如何生成库文件网上有很多教程。

在编译完成后,在目录out\target\common\obj\JAVA_LIBRARIES\中,会生成一个rabbit_intermediates文件夹,其中会包含3个库文件,我们需要的是class-header.jar和javalib.jar。

这里的class-header.jar就是后面会用到的rabbitsrc.jar,主要用来给其他依赖Rabbit模块的模块添加依赖用于通过编译,也可以对上层应用开放,所需要的权限问题网上也有相关资料。 javalib.jar则是后面用到的rabbitdex.jar,主要用来编译并以BootJar的形式安装在系统中,不难理解,毕竟只有壳没有内核也是不行的。

在这里插入图片描述 PS:当然如果不用隐藏源码,直接使用classes.jar,里面是包含所有源代码的class文件,会省去很多工作量,也并非本文所描述的需求。

接下来,在模块Rabbit的bp文件中,就可以直接删掉源代码,然后在BP文件中添加对应的编译语句了。

// 1. 引入接口Jar,并再生成一个其他模块所需要依赖的库 // ==== import library ==== java_import { name: "rabbitlibsrc", jars: ["libs/rabbitsrc.jar"], } // ==== Java library ==== java_library { name: "RabbitSrc", static_libs: [ "rabbitlibsrc", ], } // 2. 引入Dex Jar,并安装到系统中 // ==== import library ==== java_import { name: "rabbitlibdex", jars: ["libs/rabbitdex.jar"], } // ==== Java library ==== java_library { name: "RabbitDex", static_libs: [ "rabbitdex", ], installable: true, } // 3. 添加dex Jar编译的Boot Jar所需的权限文件预编译 // ==== ETC permissions ==== prebuilt_etc { name: "RabbitDex.xml", src: "RabbitDex.xml", sub_dir: "permissions", }

RabbitDex.xml: 放在和Android.bp同目录下

PS:这里可能会有疑问,能否封装一个Jar C,既有接口文件,又有dex文件呢?

那么,可不可以一个Jar包中既有class文件又有dex文件呢,理论上是可以的,但是在实际编译过程中遇到了这样的报错。

containing both DEX and Java-bytecode content

Step 2 device/company/project/package.mk(随便编的,根据具体项目定)中加入

PRODUCT_PACKAGES += RabbitDex \ RabbitDex.xml

Step 3 继续在该mk文件中PRODUCT_BOOT_JARS处加入Rabbit,如果没有现成的PRODUCT_BOOT_JARS可以直接添加下面的语句。

PRODUCT_BOOT_JARS += RabbitDex

Step 4 编译刷机

可能出现的问题

(1)编译错误 installable+static_libs引用导致,原理未知。

Java java.lang.ArrayIndexOutOfBoundsException: 0

解决:去除installable或者用libs取代static_libs。

(2)运行时错误

java.lang.NoClassDefFoundError: Failed resolution of XXX

解决:该报错说明已经找到了Jar包中的类,但是缺少方法实现,对于该需求,应重点检查在引用Jar包的地方是否引用成了接口Jar包而不是Dex Jar包。如果确实是引用的Dex Jar包,则要检查生成Jar包的源码和过程是否有问题,以及权限文件是否加入了编译。

(2)运行时错误

java.lang.ClassNotFoundException:XXX

解决:该报错说明Jar包未正确添加,可以现在adb shell里的system/framework中查看是否已存在所需的Jar包。以及引用Jar包的地方如果未提前预编译,是否是通过ClassLoader来加载的,ClassLoader的使用方法可以参考SystemServer.java。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有