TWRP Recovery的强悍,使得它成为了刷机畛域当之无愧的首选。很多设施刷机的第一步,正是抉择一款适宜的TWRP,而后刷上去。目前,多个品牌的热门机型都有官网适配了,且一些开发者也给官网未笼罩的机型适配了本人的非官方版本。
然而,开发者们并不是万能的,总有那么一些机型,并没有哪一位开发者前来适配。在这样的情境下,你是违心痴痴地等,等到哪位大神有工夫做适配,还是马上入手饥寒交迫呢?
当然要本人入手啦!
事实上,TWRP的适配并没有设想中的那么难。实践上只需在Android的源代码中进行,筹备好必要的文件,运行编译命令,即可实现适配。上面笔者就来联合本人的教训,一步步解说如何适配TWRP Recovery。
<!--more-->
配置要求
编译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# 装置OpenJDKsudo apt install openjdk-8-jdk# 装置编译套件build-essential。若出错,则请应用以下第二行的命令sudo apt install build-essentialsudo apt install gcc g++ make
(二)配置ccache
ccache
是一个缓存工具,它通过将编译产生的两头文件(预处理失去的代码、输入文件*.o
等)缓存起来,待到下次编译同样源文件时间接复制而不是从新生成,以此来进步编译效率。最间接的益处,就是在make clean
之后,从新编译的速度可能快不少。
波浪线“~
”是Home目录的简写。咱们在Bash的配置文件~/.bashrc
的尾部加上以下语句,启用ccache
。ccache
默认寄存在用户目录下(~/.ccache
),能够更改环境变量CCACHE_DIR
,以设置到其余磁盘分区。
# 启用ccacheexport USE_CCACHE=1# 扭转ccache缓存门路export CCACHE_DIR=/mnt/seagate_drive/.ccache
而后重启终端,或运行source ~/.bashrc
,或新开一个Bash会话,使上述语句失效。
另外,能够设置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/reposudo chmod +x /usr/bin/repo
(二)下载OmniROM源代码
首先在磁盘中新建一个专门的目录,用于寄存OmniROM源代码,而后应用repo init
初始化源代码仓库。
mkdir omni9cd omni9repo 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-ma...。
以下载Omni 8.1的Minimal Manifest为例,依照上面的步骤就能够下载到精悍、省空间、省资源的代码树:
# 建设文件夹用来寄存代码树mkdir omni8_minimalcd omni8_minimal# 初始化源码仓库# 能够把分支名“twrp-8.1”后的“8.1”改为你想要下载的安卓版本号,反对4.4~9.0repo init -u git://github.com/minimal-manifest-twrp/platform_manifest_twrp_omni.git -b twrp-8.1 --no-clone-bundle# 开始同步repo sync -f --force-sync
留神:实测twrp-6.0、twrp-8.1能够失常编译。而最新的twrp-9.0因为谬误检测严格,会导致各种各样的编译谬误呈现,不倡议抉择!
第三步:下载TWRP源代码
留神:如果应用上文的Minimal Manifest for TWRP,则请跳过本步,因为Minimal Manifest for TWRP的manifest蕴含了TWRP源码!
而它的twrp-8.1分支也蕴含了Busybox的源码,因而无需再增加Busybox的我的项目。然而twrp-9.0却因不明起因未蕴含,不倡议抉择。
个别地,OmniROM源码树并未蕴含TWRP的源码,默认下载的是AOSP的Recovery。因而,咱们须要手动下载TWRP源码,并将其增加到repo
的仓库配置文件(manifest)中。TWRP的源码位于https://github.com/omnirom/an...。
以下有两种更改OmniROM仓库配置的计划,任选其一。(Android 9.0倡议抉择第一种,以防止意想不到的问题。)
计划一:更改本地仓库配置文件
本地仓库配置文件位于源码树的.repo/local_manifests/
目录。它本来的作用,是将OmniROM官网反对机型所波及到的所有源码仓库地址收集起来,存储到其中的roomservice.xml
中,以便于日后用repo sync
来一并进行更新。它不会随着Android官网仓库配置(.repo
目录下的其余XML manifest文件)的更新而被更改,因而倡议在其中增加与TWRP无关的仓库地址。
假如源码树根目录为omni9
。首先,进入omni9/.repo
,查看其中是否有local_manifests
这个目录,该目录中是否有roomservice.xml
这个文件。如果没有,就手动创立一个,内容如下:
<?xml version="1.0" encoding="UTF-8"?><!--Please do not manually edit this file--><manifest> <project name="OmniROM/android_bootable_recovery" path="bootable/recovery" remote="github" revision="android-9.0" /> <project name="OmniROM/android_external_busybox" path="external/busybox" remote="github" revision="android-9.0" /></manifest>
而后,对默认配置文件omni9/.repo/manifests/default.xml
进行如下批改,以删除AOSP Recovery对应的manifest我的项目:
- <project path="bootable/recovery" name="platform/bootable/recovery" groups="pdk" />+ <project path="bootable/recovery-aosp" name="platform/bootable/recovery" groups="pdk" />
最初回到omni9
目录,运行上面命令,将TWRP相干的源码下载下来:
repo sync --force-sync bootable/recoveryrepo sync --force-sync external/busybox
计划二:在默认配置文件中增加TWRP相干我的项目
这一计划在之前的Android 8.1源码中起作用。假如源码树根目录为omni8
。
首先,进入源码树,删除原先下载的AOSP Recovery:
cd omni8rm -rf bootable/recovery
而后克隆TWRP的Recovery源码:
git clone https://github.com/omnirom/android_bootable_recovery bootable/recovery
随后,删除AOSP Recovery对应的manifest我的项目。关上omni8/.repo/manifests/default.xml
,进行如下批改:
- <project path="bootable/recovery" name="platform/bootable/recovery" groups="pdk" />+ <!-- project path="bootable/recovery" name="platform/bootable/recovery" groups="pdk" /> -->
再把TWRP退出manifest。关上omni8/.repo/manifests/omni-default.xml
,在</manifest>
节的最初进行如下批改:
<project path="packages/apps/OmniSwitch" name="android_packages_apps_OmniSwitch" remote="omnirom" revision="android-8.1" /> <project path="packages/apps/OpenDelta" name="android_packages_apps_OpenDelta" remote="omnirom" revision="android-8.1" /> <project path="packages/apps/Phonograph" name="android_packages_apps_Phonograph" remote="omnirom" revision="android-8.1" />++ <project name="android_bootable_recovery" path="bootable/recovery" remote="omnirom" revision="android-8.1" />+ <project name="android_external_busybox" path="external/busybox" remote="omnirom" revision="android-8.1" /> </manifest>
这样,咱们就能够编译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源码根目录为参照。
- 因为排版限度,上面的设置未包含取值要求阐明。写着“是否”的为布尔值,取值为$true$或$false$;其余为数字值或字符串值,能够不增加引号。
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
不再实用于Android 8.0及以上版本,增加该参数会导致报错。BOARD_CUSTOM_BOOTIMG_MK
可能不受Android 8.x编译系统反对而引起报错,但在Android 9.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 partTARGET_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.defaultALLOW_MISSING_DEPENDENCIES := trueDEVICE_PACKAGE_OVERLAYS += device/huawei/berkeley/overlay# Discard inherited values and use our own instead.PRODUCT_NAME := omni_berkeleyPRODUCT_DEVICE := berkeleyPRODUCT_BRAND := HuaweiPRODUCT_MODEL := Honor View 10TARGET_VENDOR := huawei
① 继承局部
继承局部,指的是上述源文件中调用inherit-product
函数的局部,使得以后设施配置文件继承其余的配置文件。被继承的,包含ROM提供的通用(generic)配置文件(位于build/make/target
目录中),与设施专门的配置文件(通常在设施参数文件目录下,以device_<设施名>.mk
或device.mk
为文件名)。
编写时,个别只需把其余设施的代码搬来用即可,不过须要留神辨别32位和64位。
② 设施定义局部
设施定义局部是整个设施配置文件的外围,是编译系统查找设施参数文件的根据。包含以下变量,缺一不可:
变量名 | 阐明 |
---|---|
PRODUCT_DEVICE | 设施名。必须填写,这是外围参数,用于编译系统对设施名的标识! |
PRODUCT_NAME | 产品名,通常格局为omni_<PRODUCT_DEVICE> 必须填写,编译系统依据这个来为你配置编译环境! |
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.mkinclude $(BUILD_SYSTEM)/product.mkinclude $(BUILD_SYSTEM)/device.mkifneq ($(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)endifall_named_products :=# Find the product config makefile for the current product.# all_product_configs consists items like:# <product_name>:<path_to_the_product_makefile># or just <path_to_the_product_makefile> 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 := trueendifendififneq (,$(filter dump-products,$(MAKECMDGOALS)))ifeq ($(ANDROID_DUMP_PRODUCTS),all)load_all_product_makefiles := trueendifendif## 【一个设施能够领有超过一个设施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)")endififneq (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
如果是Minimal Manifest,则请设置上面的环境变量,否则会因找不到依赖而报错:
export ALLOW_MISSING_DEPENDENCIES=true
而后,运行lunch
命令,抉择编译指标。也能够间接运行lunch <上面菜单中的一个编译指标>
。
$ lunchYou're building on LinuxLunch 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-userdebugWhich 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.bpindex 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.mkindex 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.cppindex 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) << "panic: rebooting to bootloader"; // Do not queue "shutdown" trigger since we want to shutdown immediately DoReboot(ANDROID_RB_RESTART2, "reboot", "bootloader", false); }+**/++void panic() {+ LOG(ERROR) << "android::init::panic() invoked. Abort init to trigger kernel panic!";+ abort();+} static std::string init_android_dt_dir() { // Use the standard procfs-based path by default
利用补丁的办法:将上述代码保留为system/core/init/no_reboot_into_bootloader.diff
,而后在system/core/init
目录中运行以下命令:
patch -p2 < no_reboot_into_bootloader.diff
(三)功能测试与Bug修复
如果你胜利地过五关斩六将,久违的TWRP启动画面呈现在你设施的屏幕上了,那么衷心地恭喜你!接下来你要做的,就是更进一步,查看TWRP的各项性能,捕获可能影响应用的Bug。
个别地,在TWRP中,你能够进行以下单元测试:
- 挂载是否失常
进入主菜单的“Mount”(挂载),点击每个分区的选项,查看是否可能失常挂载。
- 解密是否失常
对于高通设施,察看解密是否失常的最间接办法,是看日志中是否有解密失败(decryption failed)的提醒,以及“Mount”菜单中的
data
分区是否无奈挂载。 - 是否能连贯
adb
连贯电脑,运行
adb devices
是否能检测到设施处在Recovery状态下,运行adb shell
是否能进入设施上的终端,并领有Root权限(提示符为“#
”)。 - 是否能连贯MTP
连贯电脑,查看是否有一个MTP设施呈现在“此电脑”(Windows)或文件管理器边栏(Ubuntu)中。如未呈现,在“Mount”中点击“启用MTP(Enable MTP)”。
- 是否反对OTG
2014年起的大部分支流机型都反对OTG。你能够插入一根OTG数据线,而后在TWRP终端中运行
lsusb
,看看是否检测到一个USB设施。此时再插入一个新设施,再看看lsusb
的输入里是否多出了另外一个设施。如果插入的是U盘,还能够查看/dev/block/
下是否多出了块设施sdx
(x
为任意一个小写英文字母)。 - 是否能备份/复原零碎
测试一下零碎是否能失常备份和复原,查看可选的备份分区是否蕴含必要的分区(
system
、data
、boot
、recovery
)。
注意事项
编译系统对Shell的反对
原则上,操作用的Shell只能是Bash。如果应用其余Shell,那么就会在运行一些命令时呈现难以预料的Bug——例如,在lunch
选单中,输序号抉择了设施一,最终Shell会给咱们抉择设施三,很坑。对此,编译系统会在source build/envsetup.sh
的过程中,对应用Bash之外的Shell提出正告。
然而,从Android 9.0开始,编译系统就可能反对Bash之外的第三方Shell了,比方Zsh。因而,如果你的Shell是Zsh(尤其是Oh-My-Zsh),敬请放心使用。
拓展浏览
- TWRP官网编译教程 - XDA Developers
- 实际上Mac OS X也能够。 ↩