引言:

前段时间在调试android9的系统源码,修改完了framework/service等路径下的源码后,编译生成system.img,但这种方式需要把system.img从服务器上Down下来,再让设备进入fastboot模式,线刷, 调试一把非常耗时。 于是乎网上查资料,想着能不能像apk、so那样,通过adb push的方式直接将相关的framwork.jar、service.jar等包推送到设备中,然后更新重启生效,答案显然是有的。

1、尝试1-按系统默认的.mk编译和调试----验证不可行

1.1 尝试步骤

尝试后,无效,以service.jar为例说明:( 纯步骤,没啥好啰嗦的)

#<STEP1> root
adb root
adb disable-verity
adb reboot

#<STEP2> remount
adb root
adb remount

#<STEP3> push
adb push services.jar  /system/framework/
adb push services.jar.prof  /system/framework
adb push oat/arm64/services.art  /system/framework/oat/arm64/
adb push oat/arm64/services.odex  /system/framework/oat/arm64/
adb push oat/arm64/services.vdex  /system/framework/oat/arm64/

#<STEP4> 修改service路径下源码
修改 ./frameworks/base/services/java/com/android/server 文件的private static final String TAG = "SystemServer--miles3300";,过滤miles33这个关键字 ,看日志是否有打印;

#<STEP5> rerun
adb shell sync
adb shell stop  #该命令可重启zygote
adb shell start

1.2 收获

虽然尝试了无效,但是还有些收获,如下:

1.2.1 odex vdex art区别和作用

#一、vdex
package 直接转化的 可执行二进制码 文件:
1.第一次开机就会生成在/system/app//oat/下;
2.在系统运行过程中,虚拟机将其 从 “/system/app” 下copy到 “/data/davilk-cache/” 下

#二、odex
odex 是从vdex 这个文件中 提取了部分模块生成的一个新的 可执行二进制码 文件 , odex 从vdex 中提取后,vdex 的大小就减少了。
1.第一次开机就会生成在/system/app//oat/ 下
2.在系统运行过程中,虚拟机将其 从 “/system/app” 下copy到 “/data/davilk-cache/”下
3.odex + vdex = apk的全部源码(vdex 并不是独立于odex的文件 odex + vdex才代表一个apk )

#三、art
odex进行优化生成的可执行二进制码文件,主要是apk启动的热点函数相关地址的记录,方便寻址相关
1.第一次开机不会生成在/system/app//oat/ 下,以后也不会;
2.odex 文件在运行时,虚拟机会计算函数调用频率,进行函数地址的修改;
3.最后在/data/davilk-cache/ 由虚拟机生成
4.生成art 文件后,/system/app 下的odex 和 vdex 会无效,即使你删除,apk也会正常运行
5.push 一个新的apk file 覆盖之前/system/app 下apk file ,会触发PKMS 扫描时下发force_dex flag ,强行生成新的vdex 文件 ,覆盖之前的vdex 文件,由于某种机制,这个新vdex 文件会copy到/data/dalvik-cache/下,于是art 文件也变化了。

1.2.2 感想

1、鉴于《1.2.1 odex vdex art区别和作用》的内容,想到,这也是为什么我们第一次烧完镜像开机时长会比后面几次慢的原因之一。

2、在Android升级到P之后,Google默认强制打开了pre-odex,导致本地调试的时候修改fw代码push之后不work或者开不了机器。原因在于打开pre-odex的情况下,对于bootclasspath系统会默认生成oat/art/vdex等文件,这些jar包之间相互依赖,耦合非常高。 那么有没有办法将它关掉? 答案也是显而易见–可以。

2、尝试2-修改系统默认的.mk编译并调试----验证可行

2.1 关闭预编译开关的原因

在device/xxx(如google)/xxx(项目名)/BoardConfig.mk中,将预优化的控制项WITH_DEXPREOPT := true改成false,这样在编译之后就不会进行预优化,生成的jar包也就不会是空壳。主要是art虚拟机预优化的问题。

这个变量的使能导致整个system image中的所有东西都被提前优化(pre-optimized)。这可能导致system image非常大。 但由于在启动时不再需要进行app的dex文件进行优化(dex2oat操作)从而提升其启动速度.

2.2 修改以下.mk文件

#<file> 1
build/make/target/board/generic_x86_arm/BoardConfig.mk
#<file> 2
build/make/core/dex_preopt.mk

不同的平台可能路径不一致, 不过可以通过以下方法搜索路径。

android user编译 编译安卓rom_android user编译

2.3 .mk文件中增加以下修改

diff -uNr A/build/make/target/board/generic_x86_arm/BoardConfig.mk B/build/make/target/board/generic_x86_arm/BoardConfig.mk
--- A/build/make/target/board/generic_x86_arm/BoardConfig.mk	2021-12-13 10:53:09.761212451 +0800
+++ B/build/make/target/board/generic_x86_arm/BoardConfig.mk	2022-01-11 12:38:58.964814404 +0800
@@ -38,7 +38,10 @@
 # of an SDK AVD. Note that this operation only works on Linux for now
 ifeq ($(HOST_OS),linux)
   ifeq ($(WITH_DEXPREOPT),)
-    WITH_DEXPREOPT := true
+    ##WITH_DEXPREOPT := true
+	WITH_DEXPREOPT := false
     WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY := false
   endif
 endif


diff -uNr A/build/make/core/dex_preopt.mk B/build/make/core/dex_preopt.mk
--- A/build/make/core/dex_preopt.mk	2021-12-13 10:53:09.745212451 +0800
+++ B/build/make/core/dex_preopt.mk	2022-01-13 20:05:54.907608349 +0800
@@ -34,13 +34,16 @@
 # The default values for pre-opting: always preopt PIC.
 # Conditional to building on linux, as dex2oat currently does not work on darwin.
 ifeq ($(HOST_OS),linux)
-  WITH_DEXPREOPT ?= true
+  ##WITH_DEXPREOPT ?= true
   ifeq (eng,$(TARGET_BUILD_VARIANT))
     # Don't strip for quick development turnarounds.
     DEX_PREOPT_DEFAULT := nostripping
     # For an eng build only pre-opt the boot image and system server. This gives reasonable performance
     # and still allows a simple workflow: building in frameworks/base and syncing.
    

   ifneq (false,$(WITH_DEXPREOPT_DEBUG_INFO))
@@ -49,13 +52,15 @@
 
   # Non eng linux builds must have preopt enabled so that system server doesn't run as interpreter
   # only. b/74209329
-  ifeq (,$(filter eng, $(TARGET_BUILD_VARIANT)))
-    ifneq (true,$(WITH_DEXPREOPT))
-      ifneq (true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY))
-        $(call pretty-error, DEXPREOPT must be enabled for user and userdebug builds)
-      endif
-    endif
-  endif
+  ##ifeq (,$(filter eng, $(TARGET_BUILD_VARIANT)))
+  ##  ifneq (true,$(WITH_DEXPREOPT))
+  ##    ifneq (true,$(WITH_DEXPREOPT_BOOT_IMG_AND_SYSTEM_SERVER_ONLY))
+  ##      $(call pretty-error, DEXPREOPT must be enabled for user and userdebug builds)
+  ##    endif
+  ##  endif
+  ##endif
 endif
 
 GLOBAL_DEXPREOPT_FLAGS :=

2.4 编译及push

修改完之后,全编代码,刷编译完成后的版本,后面framework.jar、service.jar 、service.core.jar的单独编译 见《2.5 如何验证》章节描述;

2.5 如何验证

2.5.1 验证service.jar 是否成功和有效

#1、我的做法修改 ./frameworks/base/services/java/com/android/server 文件的private static final String TAG = "SystemServer--miles3300";,过滤miles33这个关键字 《开机会打印》;

#2、修改/framewrok/base/service目录下面的相关文件,然后mmm ./frameworks/base/services/ 

#3、将/out/.../system/framework/services.jar和services.jar.prof  push 到系统的system/framework;

#4、 adb shell sync  / adb shell stop  #该命令可重启zygote  / adb shell start  即可

可以看修改的打印日志:

android user编译 编译安卓rom_android_02

2.5.2 验证service.core.jar 是否成功和有效

#1、我的做法修改 ./frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java 文件的DEBUG_INPUT修改为true,点击虚拟按键,过滤 interceptKeyTi 这个关键字 会打印log的;;

#2、修改/framewrok/base/service/core目录下面的相关文件,然后mmm  /framewrok/base/service/core 

#3、将/out/.../system/framework/services.core.jar  push 到系统的system/framework;

#4、 adb shell sync  / adb shell stop  #该命令可重启zygote  / adb shell start  即可

可以看修改的打印日志:

android user编译 编译安卓rom_重启_03

2.5.3 验证framework.jar 是否成功和有效

framework.jar像上面的service.jar直接编译后adb push可能会导致系统卡在开机界面或不生效,需要多加一些步骤这里