伊博,餓了麼物流移動組資深 Android 工程師,對移動端自動化測試深有研究,個人站點:www.mio4kon.com 。
前言
老闆: 小X啊,我這個手機咋沒啥特色啊.你看這個設定頁和別人的都沒啥區別.
小X: emmmm..你想要啥特色?
老闆: 就是那種能體現我的特色的特色啊.
小X: 好好好,特色是吧?安排上.
今天這篇部落格就記錄了小X是如何通過修改 Android 的系統原始碼來定製 Android 系統的.
內容主要包括下面幾點:
- 系統環境的預準備
- 下載 Android 原始碼
- 編譯 Android 原始碼
- 刷寫裝置
- 定製 Android 系統
下載 Android 原始碼並編譯網上已經有很多教程了,我這裡使用的是 mac 系統編譯,如果你也查詢過相關部落格可以知道 mac 系統編譯 Android 原始碼會遇到很多坑.網上也有利用 docker 來建立 Ubuntu Image 的方式來進行 Android 系統編譯的,例如 weishu
大佬的這篇<史上最簡單Android原始碼編譯環境搭建方法>, 利用了 docker-aosp 開源專案來進行傻瓜式的下載編譯.最開始我也嘗試使用這種方式,不過發現在新版的 mac 系統,因為 uid
的問題導致無法編譯成功(issues19), 我也沒有過多的花時間去解決這個 uid 的問題,總之最後我選擇使用了傳統的方式,尤其是第一次嘗試下載編譯的,還是不要把這個過程變成一個黑匣子.
下面我會用我的踩坑經驗來儘量讓大家避免在下載編譯,包括刷機時會遇到各種各樣的錯誤.保證按照這個教程最後都能刷機成功,不過由於環境的複雜性,可能還是會遇到其他我沒遇到的問題,這時可以 Google 之..
環境預準備
下面是我的初始環境,電腦系統也是剛重灌過的
- 系統: macOS High Sierra 10.13.6
- xcode: v9.4.1
- 手機: Nexus 6
- 終端: zsh
分盤
編譯 Android 原始碼需要保證自己的所在的分割槽格式是區分大小寫的,而一般 macOS 預設是不區分的,所以需要建立獨立的分割槽來編譯原始碼.
建立存放 dmg
的位置
cd ~
mkdir asop
複製程式碼
利用 hdiutil 建立分割槽
hdiutil create -type SPARSE -fs 'Case-sensitive Journaled HFS+' -size 200g ~/aosp/android.dmg -volname android
這裡我建立 200G 的分割槽用來下載和編譯.最終實際使用了接近 130G.所以建議有足夠大小的最好建立超過 150G 的分割槽大小.
建立掛載的快捷命令
vi ~/.zshrc
將下面內容複製到最後.
# mount the android file image
function mountAndroid { hdiutil attach ~/aosp/android.dmg.sparseimage -mountpoint /Volumes/android; }
# unmount the android file image
function umountAndroid() { hdiutil detach /Volumes/android; }
複製程式碼
應用配置 source ~/.zshrc
掛載 mountAndroid
這樣就會在 /Volumes
目錄下建立了 android
目錄.這就是之後下載原始碼的路徑,這裡我建立了子資料夾來存放: /Volumes/android/ANDROID_SPACE
xcode配置
這一段很重要,把 xcode 配好,後面會順暢很多,如果系統沒有裝 xcode 那是最好的.去下載 9 以下的版本(下載地址),這裡我下載了 8.3.3 版本.
網上很多教程是用新版本 xcode, 然後通過修改下載後的原始碼中的某配置來新增支援的方式,如下:
vi build/core/combo/mac_version.mk
新增10.13在後面 mac_sdk_versions_supported := 10.8 10.9 10.10 10.11 10.13
複製程式碼
不過這種方式最後在編譯的時候,都會有各種各樣的問題.
現在如果你和我一樣,都是使用 xcode 8.3.3 的話,還需要到 github.com/phracker/Ma… 中下載 10.11 的 mac sdk
,否則還是會在編譯時出現一些錯誤.
將下載下來的壓縮包解壓放到?目錄即可:
/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/
總結下來其實很簡單:
- 建議組合
xcode 8.3.3 + 10.11 mac sdk
- 嘗試組合
xcode 9.x.x + 10.11 mac sdk
為啥是建議 8.3.3 呢,因為我就是在這個環境下,保證不會報錯的.(踩坑過程中,有個報錯,很多人建議是用9以下的版本,實際最後才發現其實可能只是和 mac sdk
相關聯,並不是 xcode)
其他配置
確保環境變數 /opt/local/bin
顯示在 /usr/bin
之前
檢驗: echo $PATH
,否則的話將?的程式碼複製到 ~/.zshrc
中.
export PATH=/opt/local/bin:$PATH
然後再應用配置 source ~/.zshrc
安裝MacPorts
獲取 Make、Git 和 GPG 程式包
POSIXLY_CORRECT=1 sudo port install gmake libsdl git gnupg
設定fd上限
ulimit -S -n 1024
下載原始碼
我們通過 Repo 下載 Android 原始碼.
Repo 是谷歌用 Python 指令碼寫的呼叫 git 的一個指令碼, 可以用來管理多個git庫.
安裝 Repo
mkdir ~/bin
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
# 無法連線 Google 可以使用清華大學映象
curl https://mirrors.tuna.tsinghua.edu.cn/git/git-repo > ~/bin/repo(清華大學映象)
chmod a+x ~/bin/repo
複製程式碼
安裝完成後,為了方便使用可以將 repo 新增到環境變數:
#repo
export PATH=/Users/xxx/bin:$PATH
複製程式碼
下載完之後,如果網無法連線 Google , 還需要?配置:
vi ~/bin/repo
修改 REPO_URL = 'https://gerrit-googlesource.proxy.ustclug.org/git-repo'
複製程式碼
選擇編譯的版本
先切到下載目錄
cd /Volumes/android/ANDROID_SPACE
根據自己的手機機型,以及自己想安裝的版本號,來選擇具體的版本,連線如下?:
source.android.com/source/buil…
比如這裡我選擇的是如下版本(android-7.1.1_r57):
這裡要記住 NGI77B
這個程式碼,後面需要用到, 選擇好之後,初始化 repo 的配置.
repo init -u https://android.googlesource.com/platform/manifest -b android-7.1.1_r57
repo init -u https://aosp.tuna.tsinghua.edu.cn/platform/manifest -b android-7.1.1_r57 (清華大學映象)
複製程式碼
下載
repo sync
編譯原始碼
編譯準備
首先,如果你用的是 zsh, 請一定要切到bash. 直接輸入bash
即可.切記切記.
下載對應裝置硬體相關內容
下載地址?:
developers.google.com/android/dri…
對應之前我們選擇的版本的程式碼,例如我的是 NGI77B
,選擇下載對應的驅動:
將下載的檔案都解壓,其實就是三個 shell 命令,將他們拷貝到下載的 Android 目錄下.並執行:
./extract-broadcom-shamu.sh
./extract-moto-shamu.sh
./extract-qcom-shamu.sh
複製程式碼
中間需要接受相應 license,輸出 I ACCEPT
即可.
配置相應的環境變數,可以直接複製?的,需要改一改相應的目錄名
#java home
export JAVA_7_HOME=`/usr/libexec/java_home -v 1.7`
export JAVA_8_HOME=`/usr/libexec/java_home -v 1.8`
#defalut java_home
export JAVA_HOME=$JAVA_8_HOME
export PATH=${JAVA_HOME}/bin:$PATH
export ANDROID_JAVA_HOME=$JAVA_HOME
#android
export ANDROID_HOME=/Users/mio/Library/Android/sdk
export PATH=/Users/xxx/Library/Android/sdk/build-tools/27.0.3:$PATH
export PATH=/Users/xxx/Library/Android/sdk/platform-tools:/Users/mio/Library/Android/sdk/tools:$PATH
複製程式碼
ANDROID_JAVA_HOME 一定要配~~
Ps: 記得安裝 Java 和 Android sdk ,這裡不多說了
清理
make clobber
初始化編譯的環境
source build/envsetup.sh
選擇編譯目標
lunch
命令會列出可以編譯的目標,如下:
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. full_fugu-userdebug
8. aosp_fugu-userdebug
9. mini_emulator_arm64-userdebug
10. m_e_arm-userdebug
11. m_e_mips-userdebug
12. m_e_mips64-eng
13. mini_emulator_x86-userdebug
14. mini_emulator_x86_64-userdebug
15. aosp_dragon-userdebug
16. aosp_dragon-eng
17. aosp_marlin-userdebug
18. aosp_sailfish-userdebug
19. aosp_flounder-userdebug
20. aosp_angler-userdebug
21. aosp_bullhead-userdebug
22. hikey-userdebug
23. aosp_shamu-userdebug
複製程式碼
具體目標可以根據 source.android.com/source/runn… 來選擇.
因為我是 Nexus6 所以選擇 23.
編譯
make -j16
我下載的原始碼 bison
是存在 bug 的,所以需要打上修復後的 commit.可以先嚐試編譯,如果發現類似下面的錯誤可以如我所說的方式修復.
錯誤內容❌:
FAILED: /bin/bash -c "prebuilts/misc/darwin-x86/bison/bison -d --defines=out/host/darwin-x86/obj/STATIC_LIBRARIES/libaidl-common_intermediates/aidl_language_y.h -o out/host/darwin-x86/obj/STATIC_LIBRARIES/libaidl-common_intermediates/aidl_language_y.cpp system/tools/aidl/aidl_language_y.yy"
修復方法:
cd external/bison
git cherry-pick c0c852bd6fe462b148475476d9124fd740eba160
cd ../../
make bison
cp /Volumes/android/ANDROID_SPACE/out/host/darwin-x86/bin/bison /Volumes/android/ANDROID_SPACE/prebuilts/misc/darwin-x86/bison/
cd ../../
複製程式碼
然後刪除out
資料夾,再進行編譯.理論上如果之前按照我說的配置好 xcode,應該不會再報奇葩的錯誤了.
然後就是等了.應該會比下載的時候快上不少.
刷寫裝置
編譯完成後刷機其實非常簡單
如果沒有 fastboot 和 adb 執行?的程式碼:
make fastboot adb
進入 fastboot 模式
adb reboot bootloader
解鎖
fastboot flashing unlock
刷機
fastboot flashall -w
友情提示: 如果刷跪了,可以直接到?網址下載官方映象,重新刷.
修改原始碼
匯入原始碼到 Android Studio
編譯 idegen
模組,用來生成可以讓 idea/as
開啟的工程.
mmm development/tools/idegen/
./development/tools/idegen/idegen.sh
複製程式碼
執行完成後,在當前目錄下生成android.ipr
,android.iml
,android.iws
三個檔案.
其中android.ipr
檔案可以直接用 Android Studio 開啟.
但是如果所有模組都通過 AS 開啟會非常的慢,所以我們可以先編輯android.iml
,
通過?這種方式可以排除不需要的模組.
<excludeFolder url="file://$MODULE_DIR$/xxxxxx" />
例如,我不需要 prebuild
模組,便可以在該檔案裡新增下面一行配置:
<excludeFolder url="file://$MODULE_DIR$/prebuilt" />
PS: 當然我們也可以直接使用 sublime
開啟想要修改的模組.
DEBUG 原始碼
匯入到 AS 之後,例如我們要 debug com.android.settings.dashboard.SummaryLoader
這個類.我們按照平時的方式打斷點,之後點選下面的按鈕:
在出現的對話方塊中選擇 setting 程式即可.
編譯單獨的模組
上面使用的 mmm
命令實際上就是我們所說的編譯單獨模組的一個命令.如果提示找不到 mmm
命令.則需要先執行下面的命令(你肯定很熟悉:
source build/envsetup.sh
編譯原始碼常用的有下面幾種方式:
- -m 編譯所有模組
- -mm 編譯當前目錄下的模組,不包括依賴
- -mmm 編譯指定路徑下的模組,不包括依賴
- -mmma 編譯指定路徑下的模組,包括依賴
實戰一: 修改 Settings 模組
例如我們修改packages/apps/Settings/
下的 String.xml
為?:
<string name="notification_summary_none" msgid="3440195312233351409">"已允許所有應用傳送通知~~~喵"</string>
修改完成後,需要對 Settings
模組重新打包.
mmm packages/apps/Settings/
打包完成後我們需要去重新生成 system.imgae
:
make snod
image
生成後,基本就大功告成了,我們只需要將它刷到手機當中即可.
adb reboot bootloader
fastboot flash system out/target/product/shamu/system.img
複製程式碼
刷完之後就可以看到成果了?:
實戰二: 新增自己的模組
使用 Android Studio 建立一個測試專案 AuthorSample
,並將專案複製到 packages/apps
目錄下面.
由於是 AS 專案,所以我們在?目錄建立一個 Android.mk
檔案,用來配置編譯資訊.
/Volumes/android/ANDROID_SPACE/packages/apps/AuthorSample/app/src/main
Android.mk
內容如下:
#必備的兩行,宣告LOCAL_PATH變數,也就是當前的路徑
#並清除其它變數
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#包名,這生成的apk名字。AuthorSample.apk
LOCAL_PACKAGE_NAME := AuthorSample
LOCAL_CERTIFICATE := platform
#如果使用的系統的包,需要引入他們使用的資原始檔,否則會提示編譯資源找不到的錯誤
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
LOCAL_RESOURCE_DIR += frameworks/support/v7/appcompat/res
#LOCAL_RESOURCE_DIR += frameworks/support/v7/gridlayout/res
#LOCAL_RESOURCE_DIR += frameworks/support/v7/recyclerview/res
#指定該模組的編譯版本為optional
#user: 指該模組只在user版本下才編譯
#eng: 指該模組只在eng版本下才編譯
#tests: 指該模組只在tests版本下才編譯
#optional:指該模組在所有版本下都編譯
LOCAL_MODULE_TAGS := tests
#原始碼所在目錄,這裡就在當前位置的java目錄下。所以直接寫java
LOCAL_SRC_FILES := $(call all-java-files-under, java)
#LOCAL_SRC_FILES += \
# src/xx/xx/xx/XxxOne.aidl \
# src/xx/xx/xx/XxxTwo.aidl
#依賴的jar包,包括系統的和第三方的(放在libs目錄)jar包
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4 \
android-support-v7-appcompat
#android-support-v7-recyclerview
#混淆檔名
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
#關閉混淆
LOCAL_PROGUARD_ENABLED := disabled
LOCAL_AAPT_FLAGS := --auto-add-overlay
LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.appcompat
#LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.gridlayout
#LOCAL_AAPT_FLAGS += --extra-packages com.android.datetimepicker
#LOCAL_AAPT_FLAGS += --extra-packages android.support.v7.recyclerview
#打包成apk
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
複製程式碼
再利用 mmm
命令編譯該模組:
mmm packages/apps/AuthorSample/app/src/main/
編譯完成之後 apk
會生成在?目錄中:
/Volumes/android/ANDROID_SPACE/out/target/product/shamu/data/app/AuthorSample/AuthorSample.apk
我們只需要將 apk push 到手機的 /system/app/
目錄下即可.
adb root
adb remount
adb push /Volumes/android/ANDROID_SPACE/out/target/product/shamu/data/app/AuthorSample/AuthorSample.apk /system/app/
複製程式碼
重啟之後你就發現手機上已經自帶了你所寫的應用(無法解除安裝哦~~)
閱讀部落格還不過癮?
歡迎大家掃二維碼通過新增群助手,加入交流群,討論和部落格有關的技術問題,還可以和博主有更多互動
部落格轉載、線下活動及合作等問題請郵件至 shadowfly_zyl@hotmail.com 進行溝通