关于linux:TWRP-Recovery-编译教程

132次阅读

共计 21257 个字符,预计需要花费 54 分钟才能阅读完成。

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
# 装置 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 之后,从新编译的速度可能快不少。

波浪线“~”是 Home 目录的简写。咱们在 Bash 的配置文件 ~/.bashrc 的尾部加上以下语句,启用 ccacheccache 默认寄存在用户目录下(~/.ccache),能够更改环境变量CCACHE_DIR,以设置到其余磁盘分区。

# 启用 ccache
export 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/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.1android-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_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 --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/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,进行如下批改:

-  <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 p6device huawei p6
  • 内核源码:kernel p6kernel huawei p6
  • 厂商配置参数:vendor p6vendor huawei p6

值得注意的是,很多设施都有本人的代号,而开发者在 GitHub 上公布配置文件时,往往只会用代号来示意设施。如,小米 Max 的代号是 hydrogenhelium,三星 Galaxy S5 国行双卡的代号是 kltechnduo,在这样的状况下,你就不能用mimaxgalaxy s5G9008W为关键字来搜寻配置文件。要查找代号与设施的对应关系,你能够去魔趣下载页面、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.imgrecovery.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_8888RGB_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_hdpiportrait_mdpilandscape_hdpilandscape_mdpiwatch_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 传递给启动映像(bootrecovery)的指令,能够管制它们启动的行为。
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_< 设施名 >.mkdevice.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.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:
# <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 := 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
总的调用关系

根据上述剖析,咱们不难总结出上面的调用关系:

  1. build/make/core/product_config.mk读取设施参数文件目录 device/ 下所有的AndroidProducts.mk,失去设施 Makefile 的列表。
  2. product_config.mk在列表中搜寻用户指定设施(如华为光荣 10 View)的设施 Makefile(omni_berkeley.mk)。
  3. 若能搜寻到,则从设施 Makefile 中读取设施信息,配置好编译环境;否则报错退出。

第六步:开始编译

配置文件批改实现后,咱们就能够立即开始编译了。

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

source build/envsetup.sh

如果是 Minimal Manifest,则请设置上面的环境变量,否则会因找不到依赖而报错:

export ALLOW_MISSING_DEPENDENCIES=true

而后,运行 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) << "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/ 下是否多出了块设施 sdxx 为任意一个小写英文字母)。

  • 是否能备份 / 复原零碎

    测试一下零碎是否能失常备份和复原,查看可选的备份分区是否蕴含必要的分区(systemdatabootrecovery)。

注意事项

编译系统对 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

  1. 实际上 Mac OS X 也能够。↩

正文完
 0