TWRP Recovery 编译适配教程

您所在的位置:网站首页 twrp的终端修改分辨率 TWRP Recovery 编译适配教程

TWRP Recovery 编译适配教程

2022-10-06 07:36| 来源: 网络整理| 查看: 265

TWRP Recovery的强悍,使得它成为了刷机领域当之无愧的首选。很多设备刷机的第一步,正是选择一款适合的TWRP,然后刷上去。目前,多个品牌的热门机型都有官方适配了,且一些开发者也给官方未覆盖的机型适配了自己的非官方版本。

然而,开发者们并不是万能的,总有那么一些机型,并没有哪一位开发者前来适配。在这样的情境下,你是愿意痴痴地等,等到哪位大神有时间做适配,还是马上动手丰衣足食呢?

当然要自己动手啦!

事实上,TWRP的适配并没有想象中的那么难。理论上只需在Android的源代码中进行,准备好必要的文件,运行编译命令,即可完成适配。下面笔者就来结合自己的经验,一步步讲解如何适配TWRP Recovery。

配置要求

编译TWRP和编译Android一样,都是相当吃系统资源的工作,因此必须确保你电脑的配置足够。运行环境只能是Linux发行版[1],下文以Ubuntu 18.04为例。

项目 要求 操作系统 64位Linux发行版,推荐Ubuntu 18.04 磁盘空间 至少30GB。Android源码相当吃磁盘空间 内存 至少4GB,推荐8GB及以上 第一步:准备编译环境 (一)安装必要的软件包

TWRP的编译,需要一系列软件包支持。在Ubuntu下,使用apt命令即可一次就安装好:

# 更新软件源 sudo apt update # 安装软件包 sudo apt install git-core gnupg flex bison gperf \ zip curl zlib1g-dev gcc-multilib g++-multilib libc6-dev-i386 \ lib32ncurses5-dev x11proto-core-dev libx11-dev lib32z-dev ccache \ libgl1-mesa-dev libxml2-utils xsltproc unzip # 安装OpenJDK sudo apt install openjdk-8-jdk # 安装编译套件build-essential。若出错,则请使用以下第二行的命令 sudo apt install build-essential sudo apt install gcc g++ make (二)配置ccache

ccache是一个缓存工具,它通过将编译产生的中间文件(预处理得到的代码、输出文件*.o等)缓存起来,待到下次编译同样源文件时直接复制而不是重新生成,以此来提高编译效率。最直接的好处,就是在make clean之后,重新编译的速度能够快不少。

在~/.bashrc的尾部加上以下语句,启用ccache。ccache默认存放在用户目录下(~/.ccache),可以更改环境变量CCACHE_DIR,以设置到其他磁盘分区。

# 启用ccache export USE_CCACHE=1 # 改变ccache缓存路径 export CCACHE_DIR=/mnt/seagate_drive/.ccache

然后重启终端,或运行source ~/bashrc,使上述语句生效。

另外,可以设置ccache缓存所占磁盘空间的大小:

ccache -M 50G 第二步:下载Android源代码

编译TWRP离不开Android源代码,因为它依赖Android源码中的组件。推荐使用OmniROM,它与TWRP的开发团队TeamWin有官方合作关系,由OmniROM持有TWRP的最新源代码。

(一)下载repo

repo是谷歌开发的软件仓库管理工具,使用Python 2.7编写,用于批量管理由Git组织的源代码。使用以下的命令,把repo下载到被PATH所包含的/usr/bin目录中:

sudo curl https://storage.googleapis.com/git-repo-downloads/repo > /usr/bin/repo sudo chmod +x /usr/bin/repo (二)下载OmniROM源代码

首先在磁盘中新建一个专门的目录,用于存放OmniROM源代码,然后使用repo init初始化源代码仓库。

mkdir omni9 cd omni9 repo init -u git://github.com/omnirom/android.git -b android-9.0

-b参数指定你需要的Android版本。一般编译3.0.x系列版本用android-6.0即可,但是更新的版本则需要android-7.0及更高。笔者强烈建议只选择最新的TWRP版本——3.3.1-0,因此对应地,使用android-8.1或android-9.0。

初始化完成后,我们开始下载:

repo sync

根据网络状况和电脑性能,整个过程会需要几个小时甚至半天以上的时间,耐心等待即可。下载完成后,omni9目录中就会多出包括.repo在内的很多文件夹。

(三)小贴士

可以使用-j参数多开下载进程,适当提高下载效率。

repo sync -j8

如果下载过程中发生错误,可以加上两个参数,让repo遇到错误仍然继续下载。-f使得遇到网络错误时仍然继续,--force-sync使得遇到冲突时仍然继续。

repo sync -f --force-sync (四)备选方案——Minimal Manifest for TWRP

如果磁盘空间不足,不妨考虑Minimal Manifest for TWRP,它只包含了编译TWRP所需的最少组件,并且也包含了TWRP源码及所需依赖。项目的地址在这里:https://github.com/minimal-manifest-twrp。

以下载Omni 8.1的Minimal Manifest为例,按照下面的步骤就可以下载到精悍、省空间、省资源的代码树:

# 建立文件夹用来存放代码树 mkdir omni8_minimal cd omni8_minimal # 初始化源码仓库 # 可以把分支名“twrp-8.1”后的“8.1”改为你想要下载的安卓版本号,支持4.4~9.0 repo init -u git://github.com/minimal-manifest-twrp/platform_manifest_twrp_omni.git -b twrp-8.1 # 开始同步 repo sync -f --force-sync 第三步:下载TWRP源代码

注意:

如果使用上文的Minimal Manifest for TWRP,则请跳过本步,因为Minimal Manifest for TWRP的manifest包含了TWRP源码!

一般地,OmniROM源码树并未包含TWRP的源码,默认下载的是AOSP的Recovery。因此,我们需要手动下载TWRP源码,并将其添加到repo的仓库配置文件(manifest)中。TWRP的源码位于https://github.com/omnirom/android_bootable_recovery。

以下有两种更改OmniROM仓库配置的方案,任选其一。(Android 9.0建议选择第一种,以避免意想不到的问题。)

方案一:更改本地仓库配置文件

本地仓库配置文件位于源码树的.repo/local_manifests/目录。它原本的作用,是将OmniROM官方支持机型所涉及到的所有源码仓库地址收集起来,存储到其中的roomservice.xml中,以便于日后用repo sync来一并进行更新。它不会随着Android官方仓库配置(.repo目录下的其他XML manifest文件)的更新而被更改,因此建议在其中添加与TWRP有关的仓库地址。

假设源码树根目录为omni9。首先,进入omni9/.repo,检查其中是否有local_manifests这个目录,该目录中是否有roomservice.xml这个文件。如果没有,就手动创建一个,内容如下:

然后,对默认配置文件omni9/.repo/manifests/default.xml进行如下修改,以删除AOSP Recovery对应的manifest项目:

- +

最后回到omni9目录,运行下面命令,将TWRP相关的源码下载下来:

repo sync --force-sync bootable/recovery repo sync --force-sync external/busybox 方案二:在默认配置文件中添加TWRP相关项目

这一方案在之前的Android 8.1源码中起作用。假设源码树根目录为omni8。

首先,进入源码树,删除原先下载的AOSP Recovery:

cd omni8 rm -rf bootable/recovery

然后克隆TWRP的Recovery源码:

git clone https://github.com/omnirom/android_bootable_recovery bootable/recovery

随后,删除AOSP Recovery对应的manifest项目。打开omni8/.repo/manifests/default.xml,进行如下修改:

- +

再把TWRP加入manifest。打开omni8/.repo/manifests/omni-default.xml,在节的最后进行如下修改:

+ + +

这样,我们就可以编译TWRP了;并且下一次我们也能通过repo sync,将TWRP一并更新。

第四步:收集配置文件 (一)配置文件的组成

编译TWRP,离不开设备的配置文件。设备配置文件一般包括以下部分,以下所提及的路径均以Android源码根目录为参照:

设备配置参数

设备配置参数位于device目录下,定义设备的一系列基本信息。它由一系列Makefile文件(*.mk)与设备特定的源代码组成。

内核源码

内核源码位于kernel目录下,会在Android编译的同时一并编译。值得注意的是,并不是所有的设备都有对应的源代码,有些设备使用预编译的内核(prebuilt kernel),一般位于配置参数目录中。

厂商配置参数

厂商配置参数位于vendor目录下,存放厂商特定的配置信息、预编译的各种文件(可执行文件、运行库等,通常不开源)等。相当一部分设备只需在编译整个Android系统时才须用到厂商配置文件,编译TWRP时不需要。

(二)在GitHub上搜索配置文件

怎样获得你设备的配置文件呢?去GitHub吧!GitHub上一般都会有各种设备的各类配置文件,善用搜索即可。例如,笔者的手机是华为P6(型号为P6-C00),那么在GitHub中,使用以下关键字搜索上述三类配置文件:

配置参数:device p6或device huawei p6 内核源码:kernel p6或kernel huawei p6 厂商配置参数:vendor p6或vendor huawei p6

值得注意的是,很多设备都有自己的代号,而开发者在GitHub上发布配置文件时,往往只会用代号来表示设备。如,小米Max的代号是hydrogen或helium,三星Galaxy S5 国行双卡的代号是kltechnduo,在这样的情况下,你就不能用mimax、galaxy s5或G9008W为关键字来搜索配置文件。要查找代号与设备的对应关系,你可以去魔趣下载页面、Lineage OS下载页面、Resurrection Remix下载页面等开源ROM网站查询之。

不同的开源ROM,所适用的配置文件往往各不相同,配置文件中支持的参数也往往各异。由于我们使用OmniROM作为编译TWRP的载体,因此最好能找到适用于OmniROM的配置文件——也就是配置参数目录中带有omni_.mk的那一个。如果实在找不到,则请参阅下一步“修改配置文件”中的方法。

(三)把配置文件放到相应目录下

在GitHub上找到一个可用的repository之后,直接git clone到相应位置即可。如何确认“相应位置”?其实很简单。

设备配置参数目录的规范是device//,内核源码和厂商配置参数的路径类似。例如,笔者手上华为P6的配置文件目录如下:

设备配置参数:device/huawei/hwp6_u06 内核源码:kernel/huawei/hwp6_u06 厂商配置参数:vendor/huawei

根据AOSP的规则,Android源码目录下的各个repository有固定的命名规范,这一规范就是将上述路径的“/”换成“_”,因此也很容易猜出你找到的repository该放在哪个目录。但是如果你碰到未按规范命名的repository,则必须按照上面的规则,推知你该放置的目标目录。

第五步:修改配置文件

我们获取到的现成的配置文件,不一定都是开箱即用的。它们在诞生之初,并不都是为我们现成的这套Android源代码设计的,能开箱即用的仅限于少数热门机型,大部分配置文件只适用于不同版本的OmniROM,甚至其他的ROM——典型的如CyanogenMod。因此,修改配置文件,是适配TWRP的必修课。

(一)判断配置文件是否能够直接适用

如果你的设备幸运地为TWRP官方所支持,那么在修改配置文件上,你就不必花费太多功夫,开箱使用即可。可以在TWRP官网中查找你的机型。

若不为官方所支持,也不用灰心,可能会有开发者进行非官方适配。只需在GitHub你找到的设备参数repository中,查看是否有当前OmniROM版本对应的分支(branch)。理论上,适合于Android 7.0及以上的版本可直接套用于OmniROM 8.1。

(二)修改BoardConfig.mk

BoardConfig.mk是设备参数文件的组成部分,其中存放着不少与boot.img与Recovery编译的参数。正确设置这些参数,是保证TWRP正常编译的前提。Recovery和boot.img性质相同,均为Android的启动映像。

注:

以下所有目录均以Android源码根目录为参照。 由于排版限制,下面的设置未包括取值要求说明。写着“是否”的为布尔值,取值为truefalse;其余为数字值或字符串值,可以不添加引号。 1. 内核打包参数

这些参数,控制着内核映像文件(kernel image)打包进入启动映像的工作。它们一般都被开发者提前设置好,不需改动。启动映像通过mkbootimg生成,它的源代码位于system/core/mkbootimg中。

参数名 说明 BOARD_KERNEL_CMDLINE 内核的运行参数 BOARD_KERNEL_BASE 内核在启动映像中的基址 BOARD_KERNEL_PAGESIZE 内核的页面大小 BOARD_MKBOOTIMG_ARGS 需要传递给mkbootimg工具的额外参数 BOARD_BOOTIMAGE_PARTITION_SIZE 启动分区的大小 BOARD_RECOVERYIMAGE_PARTITION_SIZE Recovery分区的大小 BOARD_CUSTOM_MKBOOTIMG 一些特殊的设备需要用专门的mkbootimg工具来生成启动映像(如瑞芯微)。在这里指定该工具的路径。 BOARD_CUSTOM_BOOTIMG_MK 对于一些格式特殊的启动镜像,用户可以自己编写Makefile。在这里指定自定义的Makefile文件路径。

注意:BOARD_CUSTOM_MKBOOTIMG和BOARD_CUSTOM_BOOTIMG_MK不再适用于Android 8.0及以上版本,添加该参数会导致报错!

2. 内核编译参数

各类第三方开源ROM的开发者都建议你自己编译内核,而不是使用设备参数文件中预先编译好的内核映像(prebuilt kernel image,预编译内核)。这是因为已编译的内核映像无法修改,只适用于某个特定版本的系统,一旦放到一个新系统中就无法正常工作,甚至直接无法开机。如果你找到的设备参数文件提供了预编译的内核,且你能够找到内核源码,请设置下面的选项。

参数名 说明 TARGET_KERNEL_SOURCE 指定内核源码所在的目录 TARGET_KERNEL_CONFIG 指定编译内核使用的配置文件。配置文件位于内核源码arch//configs中 BOARD_KERNEL_IMAGE_NAME 指定内核映像名。Android编译系统根据它来查找内核映像编译而成的内核映像位于内核源码arch//boot中 KERNEL_TOOLCHAIN 指定用于编译内核的交叉工具链。有些设备比较特殊,使用Android源码自带的编译器编译的内核无法启动,必须使用专用或旧版本的编译器 TARGET_KERNEL_CROSS_COMPILE_PREFIX 与KERNEL_TOOLCHAIN配合使用,指定交叉工具链的前缀

但是,如果你实在无法找到内核源码,你也可以指定下面的参数,以使用现有的内核(如从能正常运行的boot.img与recovery.img中提取出来的内核,或设备参数文件提供者提供的内核)。不过不能保证在新版本的系统下正常使用!

参数名 说明 TARGET_PREBUILT_KERNEL 指定预编译内核的路径 TARGET_PREBUILT_RECOVERY_KERNEL 指定用于Recovery的预编译内核路径

注意:

内核源码与预编译内核只能二选一,不能同时设置上面两个表格中的所有参数! 根据不同平台,BOARD_KERNEL_IMAGE_NAME会有不同的取值。ARM平台为zImage,x86平台为bzImage,采用U-Boot的平台(如NXP、树莓派)为uImage。 3. Recovery相关选项

BoardConfig.mk中也包括了设置Recovery的若干选项,其中主要的参数如下所示。一般设备参数文件提供者都已经设置好了相应的参数。

参数名 说明 TARGET_RECOVERY_PIXEL_FORMAT 指定Recovery显示的像素格式。不同的设备有不同的像素格式,常见的有RGB_8888、RGB_565等,设置不当会引起花屏、黑屏等故障。 TARGET_RECOVERY_FSTAB 指定Recovery分区表信息文件(fstab)的路径。该文件记载了可供挂载的分区信息,用户可在Recovery的“挂载”页面中选择是否挂载它们。 BOARD_RECOVERY_SWIPE 启用滑动操作,在非触屏Recovery中可以允许用户上下滑动屏幕来移动高亮选项。一般启用。 DEVICE_RESOLUTION 指定设备的分辨率。 RECOVERY_GRAPHICS_USE_LINELENGTH Recovery图形显示时使用“行距”。具体作用笔者尚还不清楚,但是该选项若设置不当,会导致Recovery花屏。 BOARD_HAS_SDCARD_INTERNAL 设置设备是否有内置SD卡。现阶段的新设备均拥有至少8GB的eMMC存储,都将内置存储的/data/media/0划为内置SD卡。 RECOVERY_SDCARD_ON_DATA 在Recovery中,确定SD卡位于data分区。现阶段的新设备都将内置存储的/data/media/0划为内置SD卡。与上面的BOARD_HAS_SDCARD_INTERNAL呼应。 TARGET_RECOVERY_INITRC 指定自己的init.rc路径。init.rc是Android初始化程序init最主要的脚本,起到main()函数的作用。该选项允许用户编写自己的init.rc,以支持各种客制化的设备平台。

注意:TARGET_RECOVERY_INITRC 仅适用于AOSP官方Recovery,以及Android 6.0之前的旧版本Recovery(如TWRP 2.x、ClockworkMod)。新版本的TWRP(≥3.0)会直接忽略该选项,只使用它提供的init.rc。

4. TWRP专用选项

TWRP有专属的一些选项,部分选项如下所示。

参数名 说明 TW_THEME 指定TWRP的主题。不同的主题决定TWRP显示的不同样式,包括分辨率、屏幕方向等。默认可选的主题有:portrait_hdpi、portrait_mdpi、landscape_hdpi、landscape_mdpi、watch_mdpi。必须设置,否则编译过程中TWRP的编译规则会报错! TW_CUSTOM_BATTERY_PATH 指定电池路径。电池路径为内核系统目录/sys中电池设备所在的路径,TWRP访问它以显示电池电量。例:华为P6的路径是/sys/devices/platform/bq_bci_battery.1/power_supply/Battery。 TW_BRIGHTNESS_PATH 指定亮度路径。亮度路径为内核系统目录/sys中屏幕调节文件所在的路径,TWRP编辑它以更改屏幕亮度。例:华为P6的路径是/sys/devices/platform/k3_fb.1/leds/lcd_backlight0/brightness。 TW_DEFAULT_BRIGHTNESS 指定默认亮度。取值范围为[0,255]。 TW_MAX_BRIGHTNESS 指定最大亮度。取值范围为[0,255]。 TW_FLASH_FROM_STORAGE 该参数作用未知,可能仅适用于2.x版本。在3.2.3-0版本中已经失效。 TW_EXTERNAL_STORAGE_PATH 指定外部存储器的挂载路径。 TW_EXTERNAL_STORAGE_MOUNT_POINT 指定外部存储器的挂载点。 TW_DEFAULT_EXTERNAL_STORAGE 指定是否将默认存储器设为外置存储。在3.2.3-0版本中已经失效。 TW_EXCLUDE_SUPERSU 指定是否不包含SuperSU。包含了SuperSU的TWRP会在每次重启时提示用户Root手机。 TW_INCLUDE_NTFS_3G 指定是否包含NTFS-3G模块,以支持NTFS分区。 TW_IGNORE_MISC_WIPE_DATA 指定是否忽略从Bootloader传递而来的清除data分区的指令。这里的misc分区存放了Bootloader传递给启动映像(boot或recovery)的指令,可以控制它们启动的行为。 TW_EXTRA_LANGUAGES 指定是否增加额外的语言。额外的语言包括中文、日本语等。默认情况下TWRP只会包含英语与若干欧洲语言(如德语、法语、俄语、丹麦语等)。 5. 加密相关选项

现今能购买到的手机,大多都已对data分区进行了加密,要想在系统中读取data分区,必须有一个解密的过程。官方系统(包括Recovery)的启动就包含了解密过程;而TWRP要想读取data分区,则必须设置好下面的选项,并包含用于解密的其他组件。

笔者知道的加密方案有两种:高通的QSEECOM加密,与华为的专用文件系统强制加密(基于F2FS)。其中只有前者受到TWRP广大开发者的支持,TWRP的很多大神都已给自己负责的机型加入了高通的加密组件。具体给你的高通处理器机型增加加密功能的方法,笔者会择日写上教程。(给小米Max的官方TWRP适配高通解密组件的开发者,就是我!)

参数名 说明 TW_INCLUDE_CRYPTO 指定TWRP是否包含加密组件,并启用加密解密功能 TARGET_HW_DISK_ENCRYPTION 指定设备是否包含硬件加密功能。现阶段启用加密的设备,一般都是硬件加密 TARGET_KEYMASTER_WAIT_FOR_QSEE 对于高通方案,指定在Recovery启动时是否等待高通加密服务程序qseecomd完成解密。必须开启,否则TWRP的解密功能形同虚设 6. 调试相关选项

TWRP支持logcat调试功能,可以如同在Android系统里一样读取logcat日志。

参数名 说明 TWRP_INCLUDE_LOGCAT 指定是否在TWRP中包含logcat TARGET_USES_LOGD 指定是否在TWRP中启用日志服务logd (三)对非OmniROM配置文件的修改

并不是所有的设备都拥有适用于OmniROM的配置文件,因此还需将适用于其他ROM的配置文件进行一番修改。这种情况通常出现在年代略微久远的老设备上,它们往往只有CyanogenMod 4.x等老版本ROM的配置文件。不过,修改过程并不复杂。

1. 修改设备Makefile(product Makefile)

每个ROM的设备参数文件都有一个ROM特定的“设备 Makefile”,它定义了设备的基本信息,起到当之无愧的“门户”作用。

设备Makefile的定义

我们来分析一下这类Makefile。它可以分为三个部分——继承部分、设备定义部分、用户自定义部分。一个示例的设备Makefile如下所示(设备为华为荣耀10 View,省略开头的Apache 2.0协议内容):

# # This file is the build configuration for a full Android # build for grouper hardware. This cleanly combines a set of # device-specific aspects (drivers) with a device-agnostic # product configuration (apps). # # Sample: This is where we'd set a backup provider if we had one # $(call inherit-product, device/sample/products/backup_overlay.mk) $(call inherit-product, $(SRC_TARGET_DIR)/product/core_64_bit.mk) # Get the prebuilt list of APNs $(call inherit-product, vendor/omni/config/gsm.mk) # Inherit from the common Open Source product configuration $(call inherit-product, $(SRC_TARGET_DIR)/product/aosp_base_telephony.mk) #treble $(call inherit-product, $(SRC_TARGET_DIR)/product/treble_common.mk) # must be before including omni part TARGET_BOOTANIMATION_SIZE := 1080p # Inherit from our custom product configuration $(call inherit-product, vendor/omni/config/common.mk) # Inherit from hardware-specific part of the product configuration $(call inherit-product, device/huawei/berkeley/device.mk) PRODUCT_PROPERTY_OVERRIDES += ro.hardware.nfc_nci=nqx.default ALLOW_MISSING_DEPENDENCIES := true DEVICE_PACKAGE_OVERLAYS += device/huawei/berkeley/overlay # Discard inherited values and use our own instead. PRODUCT_NAME := omni_berkeley PRODUCT_DEVICE := berkeley PRODUCT_BRAND := Huawei PRODUCT_MODEL := Honor View 10 TARGET_VENDOR := huawei ① 继承部分

继承部分,指的是上述源文件中调用inherit-product函数的部分,使得当前设备配置文件继承其他的配置文件。被继承的,包括ROM提供的通用(generic)配置文件(位于build/make/target目录中),与设备专门的配置文件(通常在设备参数文件目录下,以device_.mk或device.mk为文件名)。

编写时,一般只需把其他设备的代码搬来用即可,不过需要注意区分32位和64位。

② 设备定义部分

设备定义部分是整个设备配置文件的核心,是编译系统查找设备参数文件的依据。包括以下变量,缺一不可:

变量名 说明 PRODUCT_DEVICE 设备名。必须填写,这是核心参数,用于编译系统对设备名的标识! PRODUCT_NAME 产品名,通常格式为omni_必须填写,编译系统根据这个来为你配置编译环境! PRODUCT_BRAND 品牌名 PRODUCT_MODEL 设备型号。会显示在系统设置的“关于设备”中 TARGET_VENDOR 厂商名 ③ 用户自定义部分

以上两类代码之外的其他代码,就属于用户自定义的代码了。可以放置其他的参数。

给设备Makefile改名

根据ROM的不同,设备Makefile会有不同规格的文件名。常见的如下所示:

OmniROM: omni_.mk CyanogenMod: cm.mk 魔趣:mokee.mk(旧版本)、mk_.mk(Android 9)

尽管文件名不同,但它们实际上大同小异。只需将名字统一重命名为omni_.mk即可。注意文件名中的“设备名”即该文件中PRODUCT_DEVICE的值,否则编译系统会报错找不到文件并中止。

2. 明确设备参数文件Makefile的调用链

Makefile(*.mk)的调用,存在一个链的关系。这条调用链的起点是编译系统负责管理设备配置文件的Makefile程序——build/make/core/product_config.mk,它会检测设备配置文件目录中的AndroidProducts.mk,并根据AndroidProducts.mk来定位到设备的“元Makefile”。

AndroidProducts.mk

AndroidProducts.mk是设备配置文件的入口。编译系统通过查找device目录下所有的AndroidProducts.mk,来确定某一款设备确实有其配置文件存在。该文件用于将设备Makefile包含进来,包含的方法是将它传递给变量PRODUCT_MAKEFILES。通常的写法如下所示:

PRODUCT_MAKEFILES := \ $(LOCAL_DIR)/omni_berkeley.mk product_config.mk

product_config.mk工作逻辑的其中一步,就是在接受用户输入的设备配置名后,在device/目录中检索配置文件,若配置文件不存在,就会报错。相关代码如下所示(保留原始注释,并增加笔者的进一步说明):

# --------------------------------------------------------------- # Include the product definitions. # We need to do this to translate TARGET_PRODUCT into its # underlying TARGET_DEVICE before we start defining any rules. # include $(BUILD_SYSTEM)/node_fns.mk include $(BUILD_SYSTEM)/product.mk include $(BUILD_SYSTEM)/device.mk ifneq ($(strip $(TARGET_BUILD_APPS)),) # An unbundled app build needs only the core product makefiles. all_product_configs := $(call get-product-makefiles,\ $(SRC_TARGET_DIR)/product/AndroidProducts.mk) else # Read in all of the product definitions specified by the AndroidProducts.mk # files in the tree. ## 【正如上面的注释所言,这一句会搜索 device 目录下所有的 AndroidProducts.mk,然后读取这些文件中的 PRODUCT_MAKEFILES 变量,从中获得设备Makefile的路径。】 ## 【所有获取到的设备Makefile路径都会保存到 all_product_configs 这个变量中。】 all_product_configs := $(get-all-product-makefiles) endif all_named_products := # Find the product config makefile for the current product. # all_product_configs consists items like: # : # or just in case the product name is the # same as the base filename of the product config makefile. ## 【current_product_makefile 即为用户所选设备配置文件的设备Makefile文件名】 ## 【下面一长串的 foreach 函数是文本处理,将路径改为以源码根目录为参照的相对路径】 current_product_makefile := all_product_makefiles := $(foreach f, $(all_product_configs),\ $(eval _cpm_words := $(subst :,$(space),$(f)))\ $(eval _cpm_word1 := $(word 1,$(_cpm_words)))\ $(eval _cpm_word2 := $(word 2,$(_cpm_words)))\ $(if $(_cpm_word2),\ $(eval all_product_makefiles += $(_cpm_word2))\ $(eval all_named_products += $(_cpm_word1))\ $(if $(filter $(TARGET_PRODUCT),$(_cpm_word1)),\ $(eval current_product_makefile += $(_cpm_word2)),),\ $(eval all_product_makefiles += $(f))\ $(eval all_named_products += $(basename $(notdir $(f))))\ $(if $(filter $(TARGET_PRODUCT),$(basename $(notdir $(f)))),\ $(eval current_product_makefile += $(f)),))) _cpm_words := _cpm_word1 := _cpm_word2 := ## 【经过上述处理后,current_product_makefile 的值就成了设备Makefile的文件名,如 omni_berkeley.mk】 ## 【如果无法获取,则该变量值为空】 current_product_makefile := $(strip $(current_product_makefile)) all_product_makefiles := $(strip $(all_product_makefiles)) load_all_product_makefiles := ifneq (,$(filter product-graph, $(MAKECMDGOALS))) ifeq ($(ANDROID_PRODUCT_GRAPH),--all) load_all_product_makefiles := true endif endif ifneq (,$(filter dump-products,$(MAKECMDGOALS))) ifeq ($(ANDROID_DUMP_PRODUCTS),all) load_all_product_makefiles := true endif endif ## 【一个设备可以拥有超过一个设备Makefile,但多数情况下只会有一个】 ifeq ($(load_all_product_makefiles),true) # Import all product makefiles. $(call import-products, $(all_product_makefiles)) else # Import just the current product. ## 【如果找不到设备Makefile,就直接报错退出】 ifndef current_product_makefile $(error Can not locate config makefile for product "$(TARGET_PRODUCT)") endif ifneq (1,$(words $(current_product_makefile))) $(error Product "$(TARGET_PRODUCT)" ambiguous: matches $(current_product_makefile)) endif $(call import-products, $(current_product_makefile)) endif # Import all or just the current product makefile 总的调用关系

根据上述分析,我们不难总结出下面的调用关系:

build/make/core/product_config.mk读取设备参数文件目录device/下所有的AndroidProducts.mk,得到设备Makefile的列表。 product_config.mk在列表中搜索用户指定设备(如华为荣耀10 View)的设备Makefile(omni_berkeley.mk)。 若能搜索到,则从设备Makefile中读取设备信息,配置好编译环境;否则报错退出。 第六步:开始编译

配置文件修改完成后,我们就可以立刻开始编译了。

在Android源码根目录下,首先初始化编译环境:

source build/envsetup.sh

然后,运行lunch命令,选择编译目标。也可以直接运行lunch 。

$ lunch You're building on Linux Lunch menu... pick a combo: 1. aosp_arm-eng 2. aosp_arm64-eng 3. aosp_mips-eng 4. aosp_mips64-eng 5. aosp_x86-eng 6. aosp_x86_64-eng 7. omni_berkeley-user 8. omni_berkeley-userdebug 9. omni_berkeley-eng 10. omni_hwp6_u06-userdebug 11. omni_hwp6_u06-eng 12. omni_kenzo-userdebug 13. omni_emulator-userdebug Which would you like? [aosp_arm-eng] 10

最后,开始编译TWRP:

make recoveryimage

编译而成的Recovery为out/target/product//recovery.img,直接使用fastboot等工具刷入即可。

第七步:调试查错,修改代码

适配TWRP永远都不会是一个一蹴而就的事情,这就意味着你不可能一次就成功。潜在的各种错误,会潜伏在编译过程、运行过程乃至启动成功后的每一个角落,你要做的,就是随时查错。

(一)编译时出错

编译过程中出错,是最容易排查的。一言以蔽之,就是——“发现一个错误,解决一个错误”。所有的错误,都会在终端输出中显示出来,你只需要观察错误的输出,然后根据它的提示解决即可。自Android 7.0起引入的ninja构建工具,会在出错时输出产生错误的命令,以“FAILED:”前缀标明之,只需在出错时搜索“FAILED:”,即可快速定位出错点。

(二)运行前出错

成功通过编译后,得到的Recovery很可能不能正常启动,表现为自动重启、黑屏等。这个时候,最直接的查错办法,就是拿到内核日志。

1. 获取内核日志的方法

一般来说,内核只要出现了panic,就会“想方设法”把崩溃时的内核日志记录下来。不同的平台、不同的内核,有不同的获取内核日志的方法。大致梳理如下。

高通:内核日志存储在/proc/last_kmsg中。 瑞芯微(如RK3188):与高通相同。 海思早期芯片(如K3V2):存储在内核参数CONFIG_APANIC_PLABEL所制定的分区中,一般是splash。

只需在正常启动的系统中(或将另一个正常启动的老Recovery刷入boot分区)用cat命令读取它们即可。

2. init阻碍内核日志记录的坑爹设计

一些造成panic的启动故障发生于Android初始化程序init的运行过程中。然而, init有一个很坑爹的设计,就是在使用调试方式构建的ROM中,若遇到panic则自动重启进入Bootloader。官方说法是“有利于调试”,但是如此重启却会导致内核无法转存日志。因此有必要魔改掉这个功能。

打开init所在的目录system/core/init,应用如下git diff补丁即可。

diff --git a/init/Android.bp b/init/Android.bp index 45cf327f8..8a16fb2d2 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -14,6 +14,9 @@ // limitations under the License. // +// AnClark modify: I want to read logs. If handles panic as rebooting into bootloader, I won't get any logs! +// Force setting DREBOOT_BOOTLOADER_ON_PANIC=0 on product_variables. + cc_defaults { name: "init_defaults", cpp_std: "experimental", @@ -41,7 +44,7 @@ cc_defaults { "-UALLOW_PERMISSIVE_SELINUX", "-DALLOW_PERMISSIVE_SELINUX=1", "-UREBOOT_BOOTLOADER_ON_PANIC", - "-DREBOOT_BOOTLOADER_ON_PANIC=1", + "-DREBOOT_BOOTLOADER_ON_PANIC=0", "-UWORLD_WRITABLE_KMSG", "-DWORLD_WRITABLE_KMSG=1", "-UDUMP_ON_UMOUNT_FAILURE", diff --git a/init/Android.mk b/init/Android.mk index f1fe5168b..d28b4e489 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -5,10 +5,12 @@ LOCAL_PATH:= $(call my-dir) # -- ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT))) +# AnClark modify: I want to read logs. If handles panic as rebooting into bootloader, I won't get any logs! +# Force setting DREBOOT_BOOTLOADER_ON_PANIC=0 on product_variables. init_options += \ -DALLOW_LOCAL_PROP_OVERRIDE=1 \ -DALLOW_PERMISSIVE_SELINUX=1 \ - -DREBOOT_BOOTLOADER_ON_PANIC=1 \ + -DREBOOT_BOOTLOADER_ON_PANIC=0 \ -DDUMP_ON_UMOUNT_FAILURE=1 else init_options += \ diff --git a/init/util.cpp b/init/util.cpp index fdcb22d1c..a468082af 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -370,11 +370,19 @@ bool expand_props(const std::string& src, std::string* dst) { return true; } +// AnClark MODIFY: Use abort() instead of rebooting into BL to trigger panic. +/** void panic() { LOG(ERROR)


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3