編譯 Android#
使用 envsetup.sh 腳本初始化環境:source build/envsetup.sh 或者. build/envsetup.sh
如需查看可用命令的完整列表,請運行以下命令:hmm
Framework 調試#
top#
VIRT
:這個內存使用就是一個應用佔有的地址空間,只是要應用程序要求的,就全算在這裡,而不管它真的用了沒有。寫程序怕出錯,又不在乎佔用的時候,多開點內存也是很正常的;RES
: resident memory usage。常駐內存。這個值就是該應用程序真的使用的內存,但還有兩個小問題,一是有些東西可能放在交換盤上了(SWAP),二是有些內存可能是共享的;SHR
:shared memory。共享內存。就是說這一塊內存空間有可能也被其他應用程序使用著;DATA
:數據佔用的內存。這一塊是真正的該程序要求的數據空間,是真正在運行中要使用的。
vmstat#
vmstat 是一個顯示系統信息的命令。例如,它顯示主存儲器的可用容量和 CPU 的操作狀態。如果按原樣執行 vmstat 命令,則會顯示有關當前進程,內存,交換,設備,中斷和 CPU 的信息。此外,如果附加 “- d” 或 “ - p” 選項,將顯示分區和磁碟上的讀 / 寫狀態等。指定 “-f” 選項時,從系統啟動到命令執行將顯示創建進程的次數。
meminfo#
cat /proc/meminfo
free#
-b 以字節顯示容量(默認)
-k 顯示容量,以千字節為單位
-m 顯示容量,以MB為單位
-h 顯示容量單位,包含G、M
-t 還顯示物理內存和交換內存的總和
strace#
常用來跟蹤進程執行時的系統調用和所接收的信號。
time#
測量從調用指定命令到結束所花費的時間,用戶 CPU 時間和系統 CPU 時間。在指定命令的輸出結果之後,將測量結果輸出到標準錯誤輸出。命令代碼實際使用 CPU 的時間是用戶 CPU 時間。因此,如果將不存在的命令作為 time 命令的參數,則用戶 CPU 時間變為 0。睡眠時間不計算在內。
size#
顯示一個目標文件或者鏈接庫文件中的目標文件的各個段的大小
file#
確定並顯示文件類型
addr2line#
解析 so 文件
跳過開機引導#
adb shell settings put global device_provisioned 1
readelf#
查看 ELF 格式的文件信息
logcat#
adb root;adb remount
清除緩存 adb logcat -c
如果不能清除,就指定區域清除 adb logcat -c main/system/event/kernel/all(日誌緩衝區)
查看緩存 adb logcat -g
設置最大logcat緩存 adb logcat -G 100M
dump meminfo#
adb shell dumpsys meminfo
或者具體包
adb shell dumpsys meminfo packageName
printk#
- 打印調試 log
printk("%d",intA);
- 打印變量所佔內存大小
printk("sizeof(*intA)=%d",sizeof(*intA);
查看 cpu 架構#
adb shell getprop ro.product.cpu.abi
service 服務#
服務列表: adb shell service list
檢測某服務是否存在: adb shell service check SurfaceFlinger
Activity#
adb shell dumpsys activity top|grep ACTIVITY
adb shell am start -n ActivityName
獲取參數#
getprop 查看機器的全部信息參數
getprop ro.serialno 查看機器的序列號
getprop ro.carrier 查看機器的CID號
getprop ro.hardware 查看機器板子代號
getprop ro.bootloader 查看SPL(Hboot)版本號
getprop ro.build.version.release 查看系統版本(8、9...)
getprop ro.build.display.id 獲得廠商系統版本
cpu 頻率#
root權限(直接輸入su命令)
cd sys/devices/system/cpu/cpu0/cpufreq
ls文件如下
cpuinfo_cur_freq: 當前cpu正在運行的工作頻率
cpuinfo_max_freq:該文件指定了處理器能夠運行的最高工作頻率 (單位: 千赫茲)
cpuinfo_min_freq :該文件指定了處理器能夠運行的最低工作頻率 (單位: 千赫茲)
屏幕#
查看:
adb shell
wm size 獲得手機當前分辨率
wm density 獲得手機當前屏幕密度(例如560dpi)
修改:
wm size 1096*2560
wm density 420
恢復:
wm size reset
wm density reset
查看虛擬地址#
adb root;adb remount
adb shell
ps -A|grep camera 查看服務的進程號(例如4712)
cd proc/4712 進入進程號的文件夾
more maps 查看虛擬內存地址
SELinux 模式#
adb shell setenforce 0(臨時生效,關閉SELinux模式)
adb shell setenforce 1(啟用,開啟SELinux模式)
獲取路徑#
adb shell dumpsys SurfaceFlinger 最下方查看正在運行的APK
adb shell pm path "com.**" 獲取路徑
屬性系統#
android 通過 SystemProperties 的 set 和 get 方法來控制很多東西,一般上層添加一個控制開關可以使用這個方法,在系統裡面存在很多個 prop 文件。它們分別是 system/build.prop,system/etc/prop.default,vendor/build.prop,vendor/default.prop。下面分別來說下這幾個文件的構成。
- system/build.prop
這個主要是由 device\(platform) sample\product/system.prop, 還有在 build 目錄下添加的 ADDITIONAL_BUILD_PROPERTIES
- system/etc/prop.default
主要是系統添加的 PRODUCT_SYSTEM_DEFAULT_PROPERTIES
- vendor/build.prop(比較重要)
主要是系統添加的 PRODUCT_PROPERTY_OVERRIDES,添加在 device.mk 的這個屬性會被編譯到這裡,但是在 9.0 的系統,加到這裡會無效,獲取不到值。
- vendor/default.prop(會被同目錄的 build.prop 相同 property 覆蓋)
主要是系統添加的 PRODUCT_DEFAULT_PROPERTY_OVERRIDES
前綴:
- ro:只讀屬性,不能修改。
- persist:修改屬性後,重啟依然有效。數據會保存到 /data/property 目錄。其他前綴的屬性被設置後,只是保存在內存中而已,並沒有保存到磁碟,所有重啟後就恢復默認值了。
- ctrl:用來啟動和停止服務。每一項服務必須在 init.rc 中定義。init 一旦收到設置 ctrl.start 屬性的請求,屬性服務將使用該屬性值作為服務名找到該服務,啟動該服務。這項服務的啟動結果將會放入 init.svc.<服務名> 屬性中。
Android 簽名#
Android 源碼中的系統簽名統一存放路徑:build/target/product/security
-
.pem 類型文件:在 android 對 apk 簽名的時候,.pem 這種文件就是一個 X.509 的數字證書,裡面有用戶的公鑰等信息,是用來解密的。文件格式裡面不僅可以存儲數字證書,還能存各種 key, 這個可以公開,主要用於驗證某個 App 或者其它的是否由相應的私鑰簽名。
-
.pk8 類型文件:以.pk8 為擴展名的文件,應該和 PKCS 8 是對應的,用來保存 private key, 並且這個私鑰需要保密保存,不能公開。
Android 系統簽名路徑下一共有 5 種 key:
- testkey:平台默認 key。如果沒有做特殊變更的話,系統編譯默認使用該 key。由於 testkey 是公開的,任何人都可以獲取,所以存在一定的風險。如果項目由特殊需求,則一般使用自己創建 key 作為系統默認 key。
- platform:平台的核心應用簽名之一,簽名的 apk 是完成系統的核心功能。這些 apk 所在的進程 UID 是 system。manifest 節點中有添加 sharedUserId=”android.uid.system”。
- shared:這個簽名的 apk 可以和 home/contacts 進程共享數據。manifest 節點中有添加 android=”android.uid.shared”。一般用在與 tel 相關的 apk。
- media: 這個簽名的 apk 是 media/download 的一部分。manifest 節點中有添加 android=”android.media”。一般應用在 media 相關的一些 apk。每個 Apk 包會在其對應的 Android.mk 中設置 LOCAL_CERTIFICATE 屬性,指定其中一個密鑰。(如果沒有設置此變量,則默認使用 testkey)
- verity:一種特殊的系統簽名。在系統編譯時會對系統進行編譯處理。需要單獨生成
生成簽名#
在 Android 根目錄下執行命令生成 releasekey、platform、shared、media。以 platform 為例,其他 key 以相同方式生成
development/tools/make_key platform'/C=US/ST=California/L=Mountain View/O=Android/OU=Android/CN=Android/emailAddress=android@android.com'
釋義:
- 所在國家 (Country) 簡稱:C ,只能是國家字母縮寫,如中國:CN
- 所在省份 (State/Provice) 簡稱 S
- 所在城市 (Locality) 簡稱 L
- 單位名稱 (Organization Name) :簡稱 O
- 部門名稱( Organizational Unit Name (eg, section) ):簡稱 OU
- 公用名稱 (Common Name) :簡稱 CN。代碼簽名證書申請單位名稱或網站域名。
- 郵箱(Email)
系統會提示讓你輸入生成該 key 所需要的密碼,輸入回車即可。如果設置 key 密碼,則編譯不通過。
生成的 key 存在於 Android 根目錄下,把 key 移動到/build/target/product/security
下並替換之前的 key,同時需要使用 OpenSSL 的工具來驗證一下生成的 key 是否正常。以 releaseKey 為例,執行以下命令: openssl x509 -noout -subject -issuer -in releaseKey.x509.pem
生成 verity_key
- 編譯 verity:在 android 根目錄下編譯 verity。
make generate_verity_key (mmm system/extras/verity/)
- 生成 veritykey 簽名:
development/tools/make_key veritykey '/C=CN/ST=ChengDu/L=ChengDu /O=123/OU=13/CN=123/emailAddress=test@123.com'
- 執行以下命令生成.pk8 .pem 文件:
out/host/linux-x86/bin/generate_verity_key -convert veritykey.x509.pem verity_key
簽名的使用和配置
簽名的使用分為兩種情況:
- 不區分 user 和 debug 版本,僅替換當前設備編譯是所使用的 key。
- 分區 user 和 debug 版本,編譯不同版本的時候使用不同的 key。
不區分 user 和 debug 版本
- 將 android 根目錄下生成的各種.pk8 和.pem 文件 copy 到
build/target/product/security/
目錄下,覆蓋之前所有的默認 key。將 veritykey.pk8,veritykey.x509.pem,verity_key.pub 重命名.pk8,verity.x509.pem,verity_key - 修改 android 配置:
/build/core/config.mk
中定義的變量:DEFAULT_SYSTEM_DEV_CERTIFICATE := build/target/product/security/testkey
PS:PRODUCT_DEFAULT_DEV_CERTIFICATE
:mk 文件中定義的LOCAL_CERTIFICATE
變量,如果 mk 文件中定義了LOCAL_CERTIFICATE
,編譯則使用所定義的簽名文件,如果沒有定義 LOCAL_CERTIFICATE 變量,則進入 else 流程
/build/core/Makefile
中定義變量:ifeq ($(DEFAULT_SYSTEM_DEV_CERTIFICATE),build/target/product/security/releasekey) BUILD_VERSION_TAGS += release-keys
**PS:** 這段 code 主要是判斷在 config.mk 中定義的DEFAULT_SYSTEM_DEV_CERTIFICATE
變量,並在編譯的時候使用對應的 build_key。在刷機後可以通過getprop build.tags
來查看
- 修改 SELinux:
system/sepolicy/private/keys.conf system/sepolicy/prebuilts/api/{apilevel}/private/keys.conf
區分 user 和 debug 版本
以 testkey 作為示例:
device/project/
路徑下新建一個用於存放新建 key 文件的文件夾 security。僅用於存在 key 文件。後續步驟中會對路徑做條件判斷。- 在
device/project/product.mk
中進行條件判斷:獲取 TARGET_BUILD_VARIANT,如果TARGET_BUILD_VARIANT為userdebug/eng
,則使用build/make/target/product/security/testkey
;否則使用我們自定義的文件夾中的 key
Android 設備判斷系統簽名 key#
adb root;adb remount
adb shell
cd system
getprop | grep ro.build.tags
#可以看到一行 ro.build.tags=release-keys
生成三方 APP 使用的簽名文件#
在三方 App 應用中,由於不用經過 Android 系統編譯,所以如果沒有簽名文件的情況下用到特殊權限則無法安裝使用。所以可以提供對應的.keystore 文件供其使用
在自己生成的 key 所在的路徑下:
- 生成 platform.pem 文件:platform.pk8 生成了
.pem
文件:openssl pkcs8 -in platform.pk8 -inform DER -outform PEM -out platform.priv.pem -nocrypt
- 生成 platform.p12 文件:
openssl pkcs12 -export -in platform.x509.pem -out platform.p12 -inkey platform.pem -password pass:pwd -name name
,其中 name 為 alias name,pwd 為 alias pwd - 生成 platform.keystore:
keytool -importkeystore -deststorepass pwd-destkeystore ./platform.keystore -srckeystore ./platform.p12 -srcstoretype PKCS12 -srcstorepass pwd
上面三個步驟可以在該路徑下生成一個platform.keystore
文件。該文件可用於 apk 簽名使用。
Bootchart 性能工具使用方式#
Android 系統源碼中有 bootchart 的實現,路徑在system/core/init/bootchart.cpp
中,bootchart 通過內嵌在 init 進程中實現,在後台執行測量。不過 bootchart 的測量時段是 init 進程啟動之後,不包含 uboot 和 kernel 的啟動時間
bootchart 在 android 平台的使用步驟#
- 使能調試設備的 bootchart 程序,進行設備必要的開機 log:
adb shell 'touch /data/bootchart/enabled'
- 在
adb reboot
調試設備後,ubuntu 電腦連接調試設備,終端 android 根目錄執行命令:android$ ./system/core/init/grab-bootchart.sh
- bootchart 的圖標即生成在 android 根目錄
命令切橫豎屏#
#切橫屏:
su
settings put system user_rotation 3
settings put system accelerometer_rotation 0
#切豎屏:
settings put system accelerometer_rotation 1/2/ 3 隨便改
kernel log 命令#
adb shell cat proc/kmsg
修改 selinux 後 make 編譯#
make selinux_policy
:這條命令可以編譯所有 selinux ⽂件,50s 左右即可完成⼀次 makemake ⽂件名
:例如:
make plat_file_contexts -j9;
make plat_sepolicy.cil -j9;
make vendor_sepolicy.cil -j9;
make vendor_property_contexts -j9;
同步 selinux 修改到 device#
adb push out/target/product/product/vendor/etc/selinux/* vendor/etc/selinux/*
adb push out/target/product/product/system/etc/selinux/* system/etc/selinux/*
解析 crash 地址#
symbols/vendor$ addr2line -f -e bin/test_service 000000000000ac40
_ZN12UpdateSocket9handleMsgEPv
vendor/..../test.cpp:87
清除應用數據及查看版本#
清除應用數據:adb shell pm clear com.test.upgrade
查看應用版本號:dumpsys package com.test.upgrade |grep version
編譯不生成 odex 文件#
LOCAL_DEX_PREOPT := false
ANR 日誌提取#
xxx_app_anr 這個⽂件,⼀般記錄的進程堆棧信息較之 traces ⽂件少⼀些。
所以,如果有 traces,那我們就基於該⽂件分析定位 bug。
- traces ⽂件位於
/data/anr/
⽬錄下,默認情況下,每發⽣⼀次 anr,都会⽣成 traces.txt ⽂件,但是後發⽣的會將之前的覆蓋掉 - xxx_app_anr ⽂件位於
/data/system/dropbox/
⽬錄下,按不同壓縮包存放。由 Dropbox 生成
出現 ANR 可以使⽤adb pull
命令將⻋機⾥以上兩個⽬錄提取出來
串口禁止 / 恢復打印內核日誌#
通過調整 printk 的打印級別:
- 禁止打印:dmesg -n1
- 恢復打印:dmesg -n8
- 恢復禁止: echo 0 > /proc/sys/kernel/printk
- 恢復打印: echo 7 > /proc/sys/kernel/printk
打開觸摸坐標顯示#
- 打開:
settings put system pointer_location 1
- 關閉:
settings put system pointer_location 0
打開某應用#
- 查看應用包名
dumpsys package
- 打開具體包查看 activity:
dumpsys package com.***.***
- 查看包位置:
adb shell pm path com.***.***
- 打開應用:
am start ***/***.actvity
- 查看當前打開應用的 activity:
dumpsys activity top|grep ACTIVITY
獲取當前打開應用包名#
adb shell dumpsys window | grep mCurrentFocus
Selinux#
分類:
- untrusted_app: 第三方 app,沒有 Android 平台簽名,沒有 system 權限
- platform_app: 有 android 平台簽名,沒有 system 權限
- system_app: 有 android 平台簽名和 system 權限
- priv_app 沒有 platform 簽名的 app (肯定沒有 system 權限), 但 Android.mk 中
LOCAL_PRIVILEGED_MODULE := true
, 在 priv-app 目錄下的 app 查看