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 build-essential \
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
(二)配置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 omni8
cd omni8
repo init -u git://github.com/omnirom/android.git -b android-8.1
-b
參數指定你需要的Android版本。一般編譯3.0.x
系列版本用android-6.0
即可,但是更新的版本則需要android-7.0
及更高。筆者強烈建議只選擇最新的TWRP版本——3.2.3-0
,因此對應地,使用android-8.1
或android-9.0
。
初始化完成後,我們開始下載:
repo sync
根據網絡狀況和電腦性能,整個過程會需要幾個小時甚至半天以上的時間,耐心等待即可。下載完成後,omni8
目錄中就會多出包括.repo
在內的很多文件夾。
(三)小貼士
-
可以使用
-j
參數多開下載進程,適當提高下載效率。repo sync -j8
-
如果下載過程中發生錯誤,可以加上兩個參數,讓
repo
遇到錯誤仍然繼續下載。-f
使得遇到網絡錯誤時仍然繼續,--force-sync
使得遇到衝突時仍然繼續。repo sync -f --force-sync
如果磁盤空間不足,不妨考慮Minimal Manifest for TWRP,它只包含了編譯TWRP所需的最少組件。地址在這裏:https://github.com/minimal-manifest-twrp。
第三步:下載TWRP源代碼
一般地,OmniROM源碼樹並未包含TWRP的源碼,默認下載的是AOSP的Recovery。因此,我們需要手動下載TWRP源碼,並將其添加到repo
的倉庫配置文件(manifest)中。TWRP的源碼位於https://github.com/omnirom/android_bootable_recovery。
在omni8
目錄中,首先刪掉AOSP的Recovery源碼:
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 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源碼根目錄爲參照。
- 由於排版限制,下面的設置未包括取值要求說明。寫着“是否”的爲布爾值,取值爲或;其餘爲數字值或字符串值,可以不添加引號。
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編譯系統根據它來查找內核映像[2] 編譯而成的內核映像位於內核源碼 arch/<系統平臺>/boot 中 |
KERNEL_TOOLCHAIN |
指定用於編譯內核的交叉工具鏈。有些設備比較特殊,使用Android源碼自帶的編譯器編譯的內核無法啓動,必須使用專用或舊版本的編譯器 |
TARGET_KERNEL_CROSS_COMPILE_PREFIX |
與KERNEL_TOOLCHAIN 配合使用,指定交叉工具鏈的前綴 |
但是,如果你實在無法找到內核源碼,你也可以指定下面的參數,以使用現有的內核(如從能正常運行的boot.img
與recovery.img
中提取出來的內核,或設備參數文件提供者提供的內核)。不過不能保證在新版本的系統下正常使用!
參數名 | 說明 |
---|---|
TARGET_PREBUILT_KERNEL |
指定預編譯內核的路徑 |
TARGET_PREBUILT_RECOVERY_KERNEL |
指定用於Recovery的預編譯內核路徑 |
注意:內核源碼與預編譯內核只能二選一,不能同時設置上面兩個表格中的所有參數!
3. Recovery相關選項
BoardConfig.mk
中也包括了設置Recovery的若干選項,其中主要的參數如下所示。一般設備參數文件提供者都已經設置好了相應的參數。
參數名 | 說明 |
---|---|
TARGET_RECOVERY_PIXEL_FORMAT |
指定Recovery顯示的像素格式。不同的設備有不同的像素格式,常見的有RGB_8888 、RGB_565 等,設置不當會引起花屏、黑屏等故障。 |
TARGET_RECOVERY_FSTAB |
指定Recovery分區表信息文件(fstab )的路徑。該文件記載了可供掛載的分區信息,用戶可在Recovery中選擇是否掛載它們[3]。 |
BOARD_RECOVERY_SWIPE |
啓用滑動操作,在非觸屏Recovery中可以允許用戶上下滑動屏幕來移動高亮選項。一般啓用。[4] |
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 |
指定默認亮度。取值範圍爲。 |
TW_MAX_BRIGHTNESS |
指定最大亮度。取值範圍爲。 |
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的調用鏈
Makefile(*.mk
)的調用,存在一個鏈的關係。這條調用鏈的起點是Android.mk
——Android代碼樹中每個模塊的入口文件,也就是“第一道門”;另一個重要的文件是AndroidProducts.mk
,它是設備參數文件的入口,也就是“第二道門”。
Android.mk
一般地,Android.mk
內容如下。它的作用,就是與編譯系統對接,調用當前目錄下所有的Makefile,其中包括AndroidProducts.mk
、BoardConfig.mk
等。
LOCAL_PATH := $(call my-dir)
ifeq ($(TARGET_DEVICE),berkeley)
include $(call all-makefiles-under,$(LOCAL_PATH))
endif
AndroidProducts.mk
AndroidProducts.mk
用於將設備特定的Makefile(product Makefile)包含進來,將這類Makefile傳遞給變量PRODUCT_MAKEFILES
即可。一般這樣的Makefile,就是下一節將要介紹的“ROM特定的Makefile”。
PRODUCT_MAKEFILES := \
$(LOCAL_DIR)/omni_berkeley.mk
總的調用關係
根據上方對兩個Makefile的分析,我們不難得知下面的調用關係:
Android.mk
調用設備參數文件目錄下所有的Makefile,包括AndroidProducts.mk
。AndroidProducts.mk
調用設備特定的Makefile,如上例的omni_berkeley.mk
。由
omni_berkeley.mk
定義目標設備。
2. 修改ROM特定的Makefile
每個ROM的設備參數文件都有一個ROM特定的專有Makefile,它包含設備的基本信息,起到當之無愧的“門戶”作用。常見的如下所示:
- OmniROM:
omni_<設備名>.mk
- CyanogenMod:
cm.mk
- 魔趣:
mokee.mk
儘管文件名不同,但它們實際上大同小異。只需將名字統一重命名爲omni_<設備名>.mk
即可。注意設備名的準確性,否則編譯系統會報錯找不到文件並中止。
重命名之後,我們來分析一下這個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
繼承部分
繼承部分,指的是上述源文件中調用call inherit-product
函數的部分,使得當前設備配置文件繼承其他的配置文件。被繼承的,包括ROM提供的通用(generic)配置文件(位於build/make/target
目錄中),與設備專門的配置文件(通常在設備參數文件目錄下,以device_<設備名>.mk
或device.mk
爲文件名)。
編寫時,一般只需把其他設備的代碼搬來用即可,不過需要注意區分32位和64位。
設備定義部分
設備定義部分是整個設備配置文件的核心,是編譯系統查找設備參數文件的依據。包括以下變量,缺一不可:
變量名 | 說明 |
---|---|
PRODUCT_NAME |
產品名,通常格式爲omni_<設備名> 必須填寫,編譯系統根據這個來爲你配置編譯環境! |
PRODUCT_DEVICE |
設備名。這是核心參數! |
PRODUCT_BRAND |
品牌名 |
PRODUCT_MODEL |
設備型號。會顯示在系統設置的“關於設備”中 |
TARGET_VENDOR |
廠商名 |
用戶自定義部分
以上兩類代碼之外的其他代碼,就屬於用戶自定義的代碼了。可以放置其他的參數。
第六步:開始編譯
配置文件修改完成後,我們就可以立刻開始編譯了。
在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) << "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
)。