老闆:小X,我這個Android系統你安排定製下

餓了麼物流技術團隊發表於2018-10-09

伊博,餓了麼物流移動組資深 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.11mac sdk,否則還是會在編譯時出現一些錯誤.

將下載下來的壓縮包解壓放到?目錄即可:

/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/

總結下來其實很簡單:

  1. 建議組合 xcode 8.3.3 + 10.11 mac sdk
  2. 嘗試組合 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

www.macports.org/install.php

獲取 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):

老闆:小X,我這個Android系統你安排定製下

這裡要記住 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,選擇下載對應的驅動:

老闆:小X,我這個Android系統你安排定製下

將下載的檔案都解壓,其實就是三個 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這個類.我們按照平時的方式打斷點,之後點選下面的按鈕:

老闆:小X,我這個Android系統你安排定製下

在出現的對話方塊中選擇 setting 程式即可.

老闆:小X,我這個Android系統你安排定製下

編譯單獨的模組

上面使用的 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
複製程式碼

刷完之後就可以看到成果了?:

老闆:小X,我這個Android系統你安排定製下

實戰二: 新增自己的模組

使用 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/
複製程式碼

重啟之後你就發現手機上已經自帶了你所寫的應用(無法解除安裝哦~~)




閱讀部落格還不過癮?

歡迎大家掃二維碼通過新增群助手,加入交流群,討論和部落格有關的技術問題,還可以和博主有更多互動

老闆:小X,我這個Android系統你安排定製下

部落格轉載、線下活動及合作等問題請郵件至 shadowfly_zyl@hotmail.com 進行溝通

相關文章