Android Telephony原理解析与开发指南
上QQ阅读APP看书,第一时间看更新

2.4 在Google手机上调试Android源码

2.4.1 Google手机对应编译选项

Google手机至今已经发布了很多款,目前最新发布的是Google Pixel 2。本书选择Nexus 6P作为实例,读者也可以选择其他Google手机调试Android源码。

Google手机对应的代码名称、编译选项详情见表2-2。

表2-2 Google手机编译选项表代码名称

2.4.2 Google手机刷入工厂镜像

首先下载Google Nexus和Pixel系列手机的工厂镜像文件。Nexus 6P手机对应的工厂镜像是angler 8.1.0 (OPM1.171019.011, Dec 2017),即Nexus 6P手机Android 8.1.0工厂刷机镜像。

$ unzip angler-opm1.171019.011-factory-39448337.zip
$ tree angler-opm1.171019.011
angler-opm1.171019.011
├── bootloader-angler-angler-03.78.img
├── flash-all.bat
├── flash-all.sh
├── flash-base.sh
├── image-angler-opm1.171019.011.zip
└── radio-angler-angler-03.85.img

0 directories, 6 files

image-angler-opm1.171019.011.zip压缩包中包含了android-info.txt、boot.img、recovery.img、system.img、vendor.img等镜像文件。

先将Nexus 6P手机关机,然后同时按下电源键和音量键两个按键,持续几秒不要松开,就可以进入fastboot刷机模式。

$ sudo –s //一定要使用root账号刷机,否则没有权限,fastboot刷机将失败
# fastboot flashing unlock//或者fastboot flashing unlock_critical对Pixel 2 XL有效
# ./flash-all.sh
......
sending sparse 'system' 5/5 (51472 KB)...
OKAY [  1.704s]
writing 'system' 5/5...
OKAY [  0.743s]
sending 'vendor' (192545 KB)...
OKAY [  6.143s]
finished. total time: 104.914s

等待Nexus 6P手机重启完成,我们便可以体验和使用原汁原味的Android 8.1.0系统了,详情如图2-6所示。

图2-6 Nexus 6P开机向导和Android 8.1.0版本信息

2.4.3 编译本地镜像并刷入Google手机

Google手机刷入工厂镜像文件是如此得简单方便,但我们需要调试Android源码,仅刷入工厂镜像是无法办到的,因为它是用户版本的,无法调试系统级源码;因此还需要刷入本地编译出来的userdebug版本镜像文件,主要的步骤如下:

1. 下载Google手机对应的驱动文件(Driver Binaries)

到官网下载Google手机对应的驱动文件,选择Nexus 6P ("angler") binaries for Android 8.1.0 (OPM1.171019.011)的两个Driver文件:Vendor image和Qualcomm,对应的文件名分别是huawei-angler-opm1.171019.011-41db8ed5.tgz和qcom-angler-opm1.171019.011-f7e511bb.tgz,解压后是两个Shell脚本:extract-huawei-angler.sh和extract-qcom-angler.sh,将这两个文件复制到Android O源码的主目录下。

2. 将驱动文件导入到Android 8.1.0源码工程中

$ cd $oreo
$ ./extract-huawei-angler.sh //运行自解压脚本,并接受License
$ ./extract-qcom-angler.sh  //运行自解压脚本,并接受License
//将在当前目录下生成vendor目录,其中包括了华为和高通的二进制文件和对应的编译脚本
$ tree vendor -L 3
vendor
├── huawei
│   └── angler
│        ├── android-info.txt
│        ├── BoardConfigPartial.mk
│        ├── BoardConfigVendor.mk
│        ├── device-partial.mk
│        ├── device-vendor.mk
│        └── proprietary
└── qcom
    └── angler
         ├── BoardConfigPartial.mk
         ├── device-partial.mk
         └── proprietary

3. 使用angler编译选项重新编译

前面编译Android源码时,lunch选项选择的是aosp_arm64-eng。而现在导入Nexus 6P的驱动文件后,编译Nexus 6P手机对应的镜像文件时,lunch需要选择aosp_angler-userdebug,并以全新的方式编译整个代码,最简单的方式就是删除保存编译结果的out目录。

$ rm –rf out
$ source build/envsetup.sh //或者. build/envsetup.sh,
//使用第二种方法需要注意build前有一个空格
including device/asus/fugu/vendorsetup.sh
including device/generic/car/vendorsetup.sh
including device/generic/mini-emulator-arm64/vendorsetup.sh
......
including device/huawei/angler/vendorsetup.sh
including device/lge/bullhead/vendorsetup.sh
including sdk/bash_completion/adb.bash
$ lunch

You're building on Linux

Lunch menu... pick a combo:
      ......
      28. aosp_angler-userdebug
      29. aosp_bullhead-userdebug
      30. aosp_bullhead_svelte-userdebug
      31. hikey-userdebug
      32. hikey960-userdebug

Which would you like? [aosp_arm-eng] aosp_angler-userdebug//选择Nexus 6P对应的编译选项
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=8.1.0 //Android O版本
TARGET_PRODUCT=aosp_angler //lunch选择aosp_angler-userdebug
TARGET_BUILD_VARIANT=eng
......
BUILD_ID=OPM1.171019.011 //编译号
OUT_DIR=out
AUX_OS_VARIANT_LIST=
============================================
$ make –j8

4. fastboot刷入本地编译出的镜像文件

首先进入fastboot刷机模式,然后使用Android SDK中的fastboot工具刷入镜像文件,详情操作如下:

$ sudo –s //一定要使用root账号刷机,否则没有权限,fastboot刷机将失败
# fastboot flash boot boot.img
# fastboot flash system system.img
# fastboot flash vendor vendor.img
# fastboot flash userdata userdata.img
# fastboot reboot

等待手机完成重启后,验证本地编译的Android O系统:

Android Version:8.1.0
Builder Number:aosp_angler-userdebug 8.1.0 OPM1.171019.011 eng.androi20171210.
102134test-keys

• aosp_angler_userdebug——即Nexus 6P手机的userdebug版本。

• eng.androi engineer——工程模式,编译环境的用户名android(因长度限制少了字母d)。

• 20171210.102134——2017年12月10日10点21分34秒开始编译。

• test-keys——系统镜像的签名使用test-keys密钥。

详情如图2-7所示。

图2-7 Nexus 6P刷入本地编译的Android 8.1.0 userdebug系统

2.4.4 Google手机上调试Android源码

1. Android Studio导入Android O源码

首先,编译出idegen.sh脚本依赖的jar包:idegen.jar,操作详情如下。

$ mmm development/tools/idegen/ //编译idegen.jar
[100% 3/3] Install: out/host/linux-x86/framework/idegen.jar

$ development/tools/idegen/idegen.sh //当前代码主目录下将生成android.iml和android.ipr两
                                     //个Android Studio的工程配置文件
Read excludes: 15ms
Traversed tree: 70465ms

接着,打开Android Studio,进入Open an existing Android Studio project,选择android.ipr文件,开始导入Android O源码。

注意

不同的计算机处理能力不同,导入的时间也不同,需要耐心等待一段时间,由Android Studio准备Android等相关插件工具,以及建立工程的代码文件索引,以提升后续的操作性能。

2. 修改代码模块编译

本例选择com.android.phone进程加载的代码入口文件PhoneApp.java作为修改实例,其相对路径为:packages/services/Telephony/src/com/android/phone/PhoneApp.java。

在Android Studio连续快速地两次按下右Shift键,输入PhoneApp.java将快速匹配出该文件。在代码的onCreate方法中增加一行打印日志的代码,来验证代码修改后是否能成功运行在Google手机上,代码修改和编译详情如下。

@Override
public void onCreate() {
     android.util.Log.d("Android", "My Code run on the Nexus 6P");
     ......
}
$ cd $oreo
$ mmm packages/services/Telephony/
[100% 10/10] Install: out/target/product/angler/system/priv-app/TeleService/TeleService.apk

3. 挂载手机

前面成功编译了TeleService.apk文件,需要将此文件push到Nexus 6P手机上运行,在此之前还需要挂载手机,只有挂载成功以后才能push apk系统应用、系统jar包、so动态链接库等文件到手机/system挂载点。具体操作如下:

$ adb root
restarting adbd as root
$ adb remount
dm_verity is enabled on the system partition.
Use "adb disable-verity" to disable verity.
If you do not, remount may succeed, however, you will still not be able to write to
these volumes.
remount succeeded
$ adb disable-verity
Verity disabled on /system
Now reboot your device for settings to take effect//需要重启手机
$ adb reboot
$ adb root
restarting adbd as root
$ adb remount
$ remount succeeded

注意

挂载成功的手机系统重启以后,挂载的状态会失效,需要再做一次挂载操作。

4. push模块并重启应用

手机挂载成功以后,接着就要开始push应用到手机上;TeleService模块编译成功后的日志,只提示我们安装out目录下的TeleService.apk文件,其实还要安装dex相关的文件,修改才能在手机上生效。

进入out/target/product/angler/system/priv-app/TeleService/目录查证编译后文件更新的情况,除TeleService.apk文件更新了,oat目录也同时更新了,该目录下的TeleService.odex和TeleService.vdex这两个文件同样需要安装到Nexus 6P手机上对应的目录,否则我们的修改不会生效。具体操作如下:

$ tree out/target/product/angler/system/priv-app/TeleService/oat
out/target/product/angler/system/priv-app/TeleService/oat
└── arm64
     ├── TeleService.odex
     └── TeleService.vdex
$ adb push out/target/product/angler/system/priv-app/TeleService/TeleService.apk
 /system/priv-app/TeleService/
out/target/product/angler/system/priv-app/TeleService/TeleService.apk: 1 file pushed.
21.0 MB/s (7691558 bytes in 0.350s)
$ adb push out/target/product/angler/system/priv-app/TeleService/oat
 /system/priv-app/TeleService/ //重点关注push的目录没有oat
out/target/product/angler/system/priv-app/TeleService/oat/: 2 files pushed. 10.9 MB/
s (2032058 bytes in 0.178s)
$ adb reboot //重启手机或是“杀死” com.android.phone进程重启应用

注意

请读者使用adb shell命令进入手机中对应的目录,通过修改时间和文件大小来查看push的文件是否已经成功更新,同时关注push的oat目录是否在/system/priv-app/TeleService/oat目录下再次生成了oat目录。

5. 日志验证代码修改内容

$ mlog |grep -i "nexus 6p"//mlog -s Android
02-26 00:01:28.077  4671  4671 D Android : My Code run on the Nexus 6P

“02-26 00:01:28.077”:以手机上时间为准的时间戳。

“Android”:打印日志的TAG。

“My Code run on the Nexus 6P”:日志打印内容。

至此,我们已经成功搭建了Android源代码调试环境,从Android官网下载Android 8.1.0源码、Android Studio、Android SDK以及Nexus 6P手机对应的工厂镜像和驱动文件,然后成功编译本地镜像文件,刷入Android 8.1.0 userdebug版本的angler手机镜像文件,最后修改TeleService模块代码,编译后push到Nexus 6P手机上验证我们的代码修改是否生效。

2.4.5 关键问题总结

搭建Android源代码调试环境的环节众多,时间开销大,任何小细节出现问题都将无法满足要求,因此,作者总结出编译Android源码过程中自己碰到的两个非常关键的问题:Jack内存溢出和模块编译失败。

1. Jack编译器内存溢出

[  0% 38/54562] Building with Jack:
 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/with-local/classes.dex
FAILED: out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/with-local/classes.dex
/bin/bash
out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/with-local/classes.dex.rsp
Out of memory error (version 1.3-rc6 'Douarn'
(441800 22a11d4b264ae70e366aed3025ef47362d1522bb by android-jack-team@google.com)).

Jack编泽器内存不足,需要修改配置文件以增加编译器内存大小。修改源码目录下的prebuilts/sdk/tools/jack-admin文件,其中有两个参数:JACK_SERVER_VM_ARGUMENT和JACK_SERVER_COMMAND,本例中添加-Xmx4096M(配置的内存大小可根据自己计算机实际内存大小进行调整),最后执行make clean、make -j8命令重新编译。

JACK_SERVER_VM_ARGUMENTS="${JACK_SERVER_VM_ARGUMENTS:=-Dfile.encoding=UTF-8 -Xmx4096M}"
JACK_SERVER_COMMAND="java -XX:MaxJavaStackTraceDepth=-1 -Xmx4096M -Djava.io.tmpdir=
$TMPDIR $JACK_SERVER_VM_ARGUMENTS -cp $LAUNCHER_JAR $LAUNCHER_NAME"

$ ps -ef|grep jack
android  25499  2315 99 17:11 pts/0    00:01:59 java -XX:MaxJavaStackTraceDepth=
-1 -Djava.io.tmpdir=/tmp -Dfile.encoding=UTF-8 -XX:+TieredCompilation -Xmx4096M -cp
/home/android/.jack-server/launcher.jar com.android.jack.launcher.ServerLauncher
//修改了配置仍然没有生效,可以通过ps命令查看jack进程信息,手工“杀死”对应的进程,再重新执行编译命令

2. 模块编译失败

Android 8.1.0源码在使用mmm方式编译TeleService单个模块时,packages/services/Telephony/目录下包含了与测试相关的应用,但因为android-support-test Libraries依赖关系的缺失,将导致编译失败,错误信息如下:

ninja: error:
'out/target/common/obj/JAVA_LIBRARIES/android-support-test_intermediates/classes.dex.
toc', needed by 'out/target/common/obj/APPS/TeleServiceTests_intermediates/with-local/
classes.dex', missing and no known rule to make it

因此,我们只需要做简单的修改,比如在packages/services/Telephony/目录下将tests/和testapps/两个目录移除或是将这两个目录下的Android.mk文件改名为Android.mk.bak,总之与测试相关的应用或jar包不进行编译。

或者不做任何改动,直接使用make TeleService的方式编译单个模块。