framework開發實戰技巧
編譯命令詳解
-
make/mma/mmma
編譯時會把所有的依賴模塊一同編譯,mmm/mm
不會; -
通常,首次編譯時採用
make/mma/mmma
編譯; -
當依賴模塊已經編譯過的情況,則使用mmm/mm編譯。
示例:mmm frameworks/opt/net/wifi/service
,編譯wifi-service
模塊,等同於make wifi-service
單編模塊
單編鏡像
shell
make bootimage -jN
make userdataimage -jN
make systemimage -jN
make vendorimage -jN
確認模塊名稱
如何知道我修改了某個文件之後要編譯哪個模塊呢?根據當前文件所在的模塊,具體模塊名到Android.mk
或Android.bp
文件中查看,例如修改了這個文件frameworks/opt/telephony/src/java/com/android/internal/telephony/CarrierInfoManager.java
,那麼對應找telephony模塊的Android.bp文件,內容如下:
```json filegroup { name: "opt-telephony-srcs", srcs: [ "src/java/android/telephony/*/.java", ], }
filegroup { name: "opt-telephony-htmls", srcs: [ "src/java/android/telephony/*/.html", ], }
filegroup { name: "opt-telephony-common-srcs", srcs: [ "src/java/*/.java", ], }
java_library { //模塊名,java_library表示是一個jar包,所以編譯會生成telephony-common.jar name: "telephony-common", installable: true,
aidl: {
local_include_dirs: ["src/java"],
},
srcs: [
":opt-telephony-common-srcs",
"src/java/**/I*.aidl",
"src/java/**/*.logtags",
],
jarjar_rules: ":framework-jarjar-rules",
libs: [
"android.hardware.radio-V1.0-java",
"android.hardware.radio-V1.1-java",
"android.hardware.radio-V1.2-java",
"android.hardware.radio-V1.3-java",
"android.hardware.radio-V1.4-java",
"voip-common",
"ims-common",
"telephony-ext",
"services",
],
static_libs: [
"android.hardware.radio.config-V1.0-java-shallow",
"android.hardware.radio.config-V1.1-java-shallow",
"android.hardware.radio.config-V1.2-java-shallow",
"android.hardware.radio.deprecated-V1.0-java-shallow",
"telephony-protos",
"ecc-protos-lite",
],
product_variables: {
pdk: {
// enable this build only when platform library is available
enabled: false,
},
},
} ```
關於Android.bp
的語法這裏不做過多説明,可參考此篇文章《Android基礎|Android.bp語法淺析》,其中java_library
即為模塊的名稱,所以,這裏需要編譯telephony-common
,即make telephony-common
。
如果實在不知道編譯哪個模塊就整編!!!
常見的模塊
根據我個人的工作經驗,經常需要改動的目錄有兩個
-
/frameworks/
-
/packages/apps/
/packages/apps/
存放的是系統app的源碼,比如Settings
、Launcher
等,修改後直接編譯對應的app的名稱即可,app名稱也是在對應的Android.mk
裏面找LOCAL_PACKAGE_NAME
的值,比如
makefile
LOCAL_PACKAGE_NAME := Launcher3
此時可以直接make Launcher3
,關於Android.mk
的語法介紹,可以參考《Android基礎|Android.mk語法淺析》
/frameworks/
則存放的是提供給上層調用的api,比如各種View,各種Service等,此目錄稍微複雜一點,具體分一下幾種情況:
- 修改了
/frameworks/opt/
路徑下的文件
那麼直接編譯對應的子模塊就行,比如修改了/frameworks/opt/telephony/
,那麼打開telephony
下的Android.bp
文件,找到java_library
節點,下面的name屬性值就是要make的模塊名稱
- 修改了
/frameworks/base/services/
路徑下的文件
打開services下的Android.bp
文件,找到java_library
節點,下面的name屬性值就是要make的模塊名稱,如果找不到java_library
,則直接make services
即可
- 修改了
/frameworks/base/
下非services模塊文件
直接make frameworks
即可,但是這個模塊名稱要區分Android 11之前和之後,之前叫 framework
,之後叫 framework-minus-apex
- 修改了
/framework/
目錄下的res文件
make framework-res
push路徑
編譯完成後如何push文件,文件push到什麼目錄中去,這個一般可以根據編譯生成的位置來決定對應的push路徑,比如:
shell
make Settings -j16
得到結果
編譯得到Settings.apk
,push路徑為/system/product/priv-app/Settings/
,完整命令為
shell
adb push out/target/product/trinket/system/product/priv-app/Settings/Settings.apk /system/product/priv-app/Settings/
但也有不是這樣的,比如
shell
make wifi-service
得到結果
這裏並不是把wifi-events.rc
push到對應的路徑,這個時候需要找出真正的編譯產物生成到哪兒了,根據wifi-service
模塊的Android.mk
文件描述,此模塊編譯生成wifi-service.jar
,可以全局搜索wifi-service.jar
的位置
shell
find ./ -name wifi-service.jar
進入該目錄,查看一下wifi-service.jar
的創建時間是否跟編譯時間一致
跟我們編譯的時間一致,説明這個jar包就是我們剛才編譯生成的,此時就可以將此jar包push到/system/framework/
下
shell
adb push out/target/product/trinket/system/framework/wifi-service.jar /system/framework/
上面我們都是push具體文件,有時候編譯一個模塊會生成好多個文件,比如make framework
這個時候可以將編譯路徑下的所有文件都push進去
shell
adb push out/target/product/trinket/system/framework/* /system/framework/
注意,如果push路徑為私有目錄,則需要在root模式下操作,執行
shell
adb root
進入root模式以後還要將文件系統重新掛載一次
shell
adb remount
如果是新刷的系統,需要將verity驗證關閉才可以remount成功
執行
shell
adb disable-verity
關閉verity之後需要重啟一下,執行
adb reboot
上述流程我一般會整理成一個簡單的shell腳本來一鍵執行
shell
source build/envsetup.sh
lunch trinket-userdebug
make wifi-service -j16
adb root
adb remount
adb push out/target/product/trinket/system/framework/* /system/framework/
adb reboot