最近在修改Android源碼(廠商定制的源碼,非AOSP)時邻辉,遇到了一些問題:
- 新增的lib庫模塊溪王,需要依賴libart等so包肃晚,而這些so包在Android Q中被編譯到了apex中(apex文件位于/system/apex目錄)瘪匿,導致無法找到共享庫(需要修改
/system/core/roodir/etc/ld.config.txt
, 修改里面的策略比較麻煩,并且可能導致安全問題)文捶。 - 在將java層代碼移動到了
core-libart
中吱瘩,可以順利的引用到com.android.runtime.release
中的庫文件道伟,也算解決了問題1。 - 在某個最新的Android版本中使碾,system分區(qū)為erofs文件系統(tǒng)(只讀)蜜徽,無法通過adb remount來重新掛載為可讀寫。而為了替換apex票摇,需要替換
/system/apex/
下的相應文件拘鞋。該系統(tǒng)版本通過引入了overlay
文件系統(tǒng)來解決該問題。overlay文件系統(tǒng)在system掛載點上覆蓋了一層upper目錄兄朋。當用戶通過adb remount指令重新掛載后,所有對/system的操作實際是對cache
下的upper
目錄的操作怜械。由于cache分區(qū)太小颅和,導致將文件直接放入。 - 在廠商定制的Android源碼中缕允,由于存在很多二進制交付的部件峡扩,這使得單純通過編譯源碼得到的
System.img
是不完整的鏡像,無法直接刷入設備障本。
解決方案
為了將修改后的APEX文件放入設備中教届,考慮到APEX特性主要用于模塊化升級操作响鹃,肯定預留了升級模塊的功能。故而案训,可以利用APEX的升級功能來達到替換APEX的效果买置。
接下來,我們將對APEX進行簡要的學習强霎。相應的源碼位置如下
- APEX的實現(xiàn)源碼位于:
/system/apex/
模塊 - 與運行時庫相關的APEX編譯模塊(
com.android.runtime
)位于art/build/apex
模塊,編譯指令為:make com.android.runtime.release
, 編譯后替換/system/apex/
相應文件忿项,重啟后將在/apex
中掛載。
參考資料
APEX簡要介紹
APEX是Google繼Project Treble后的又一重大舉措城舞,它是Android Q的系統(tǒng)組件更新機制轩触。APEX的想法在GNU/Linux發(fā)行版中非常普遍:針對Linux庫集合中的特定部分的包更新。但這是Google從未嘗試過的事情家夺,因為Android中使用了一個ro(只讀)分區(qū)脱柱,在其中存儲了所有系統(tǒng)庫和框架,而不是大多數(shù)Linux發(fā)行版中通常使用的rw(讀寫)分區(qū)拉馋,從而使得其難以實現(xiàn)標準升級過程榨为。
庫是預編譯的代碼,可以在其它程序中使用椅邓。常用的方法是可以制作成Android應用程序調用的庫柠逞,減少Android應用程序的大小,因為不需要在多個應用程序中實現(xiàn)相同的代碼景馁。我們可以在/system/lib
和/system/lib64
目錄中找到許多預裝系統(tǒng)庫板壮。Android系統(tǒng)庫通常不會單獨更新-相反,它們會通過OTA更新作為Android平臺升級的一部分進行更新(備注:意思就是要整個系統(tǒng)升級才能把系統(tǒng)庫部分也一并升級了合住,而不能像Linux那樣子绰精,可以針對特定的系統(tǒng)庫進行升級)。另一方面透葛,Linux發(fā)行版的庫可以單獨升級笨使。
隨著APEX的推出,Android中的系統(tǒng)庫可以像Android應用程序一樣單獨更新僚害。這樣做的好處是硫椰,應用程序開發(fā)人員將能夠利用更新后的庫,而無需等待OEM推出完整的系統(tǒng)升級萨蚕。
APEX是一個生態(tài)系統(tǒng)靶草,迫使Google重新考慮Android從不同于/system的非標準分區(qū)加載所有庫和文件的方式。
首先岳遥,我們必須區(qū)分共享庫和靜態(tài)庫奕翔。共享庫是一個庫(如libkind.so的文件),它不包含自身運行所需的所有代碼,而是與實際提供代碼的其它庫進行鏈接浩蓉;靜態(tài)庫則是派继,可以認為它不依賴于其它任何庫宾袜,并在文件中靜態(tài)包含了所有內容。
Android歷史上使用了名為ld.config.txt[0]
的單個配置庫路徑文件(即Linux中的LD_LIBRARY_PATH)驾窟,以配置二進制或其它庫中所需的共享庫的允許搜索路徑庆猫。這些路徑在配置中是硬編碼的,不可擴展纫普。除非用戶安裝OTA Android更新阅悍,否則此布局(包括只讀系統(tǒng)分區(qū))會導致不可更新的庫。Google解決了該問題昨稼,允許擴展搜索路徑节视,讓單個APEX包提供自己的ld.config.txt
,其中包含額外(和更新)庫路徑假栓。
但這樣子仍存在問題:首先是ABI(應用程序二進制接口)穩(wěn)定性寻行,庫應該始終導出一組穩(wěn)定的接口,以允許其它應用程序和庫繼續(xù)使用相同的協(xié)議匾荆,即使升級后的庫也是如此拌蜘。Google正嘗試在APEXed庫之間創(chuàng)建穩(wěn)定的C接口來積極開展該工作。
APEX并不僅與庫和二進制文件牙丽。實際上简卧,它還包括配置文件、timezone更新以及一些java框架烤芦。
當前AOSP提供的APEX包名的示例有:
- com.andrid.runtime: ART和bionic runtime(二進制和庫)
- com.android.tzdata: TimeZone和ICU數(shù)據(jù)(庫和配置數(shù)據(jù))
- com.android.resolv: Android中用于解決網絡相關請求的庫(庫)
- com.android.conscrypt(Java安全提供程序)(Java框架)
如何安裝和構建APEX包
APEX軟件包是一個簡單的Zip包(如APK)举娩,可由ADB進行安裝,而后通過包管理器來安裝构罗。
apex_manifest.json
指定了package以及version:apex_payload.img
是一個micro-filesytem 鏡像(EXT 4格式)
其它文交所 用于安裝軟件包是的驗證過程的一部分铜涉,如:
- AndroidManfest.xml:
- META-INF/目錄有著包整數(shù),并使用域APK相同的機制遂唧。因此芙代,在運行用戶安裝更新之前,這些包在運行時盖彭,將由私鑰/公鑰對進行驗證纹烹。Google又增加了2層安全性,它們使用dm-verity檢測鏡像的完整性以及AVB(Android Verified Boot)驗證來確保鏡像來自可信的源召边。最壞情況下铺呵,APEX包將被拒絕。
如果所有驗證步驟都成功掌实,鏡像文件將被標記為有效陪蜻,并在下一次重新引導時替換系統(tǒng)實體邦马。
如何在啟動時安裝鏡像
預安裝的軟件包存儲在/system/apex中贱鼻,并且所有軟件包當前都是版本1.但當APEX被激活時會發(fā)生什么宴卖?我們將再次使用com.android.tzdata做例子。
讓我們重啟設備并分析logcat邻悬。
前兩行提供了足夠的信息來了解軟件包的來源與安裝位置:/apex/, Android Q引入而來新的目錄症昏,用來存儲激活的軟件包。
使用AVB成功驗證軟件包后父丰,并且公鑰匹配后肝谭,使用loop設備將APEX安裝到/dev/block/loop0,使系統(tǒng)可以訪問EXT4文件系統(tǒng)蛾扇。loop設備是一種偽設備攘烛,它是文件可以作為塊設備被訪問,使得該文件的內容可以作為安裝點被訪問镀首。
此時坟漱,由于@1后綴(表示軟件版本),APEX仍未使用更哄。為了最終讓系統(tǒng)知道我們的軟件包已經激活芋齿,它將被綁定掛載到/apex/com.android.tzdata。其中Android主動期望tzdata生效成翩,綁定裝載覆蓋不同點下的現(xiàn)有目錄或文件觅捆。
APEX的實現(xiàn)整個位于AOSP下的單個倉庫。apexd(APEX daemon)目錄含有Android上運行的代碼麻敌。apexer目錄含有構建系統(tǒng)使用的用來創(chuàng)建APEX包的代碼栅炒。
Google視圖創(chuàng)建一組APEX包的核心集,這些包可被Google更新庸论,從而可以創(chuàng)建一個在供應商之間共享的統(tǒng)一的Android基礎核心职辅,使的只有"system"更新稱為可能,但只是單個包的升級聂示。
apexd要求 /data/apex
在啟動后立即可用域携,以更新所有的Android模塊。使用FDE(全潘加密)鱼喉,/data/apex/在用戶解鎖設備之前都是加密的秀鞭,使得apex基本上毫無用處,因為只有系統(tǒng)apex的變種會在啟動時加載扛禽。除此之外锋边,所有設備都應該支持APEX,但他們需要一些內核補丁编曼。