一、認識Android的IPC主板模式
系統架構設計最關鍵的任務就是組合(或稱整合),而且最好是能與眾不同、深具創新性組合。Android就擅用了主板模式,以通用性介面實踐跨進程的IPC通訊機制。由於Android是開源開放的系統,其原始碼可成為大家觀摩的範本。首先,其主板模式提供了IBinder通用性介面。如下圖:
Android定義一個Binder父類來實現<通用性>的IBinder介面。如下圖:
然後,以Java來撰寫這個實作類別,其Java代碼如下:
// Android的原始碼// Binder.java// -------------------------------------------------------------public class Binder implements IBinder { // .......... private int mObject; public Binder() { init(); // 其它代碼 } public final boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { // 其它代碼 boolean r = onTransact(code, data, reply, flags); return r; }private boolean execTransact(int code, int dataObj, int replyObj, int flags) { Parcel data = Parcel.obtain(dataObj); Parcel reply = Parcel.obtain(replyObj); boolean res; res = onTransact(code, data, reply, flags); // 其它代碼 return res; } protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { } private native final void init();}// End
這個Binder抽象父類的主要函數:
transact()函數-- 用來實作IBinder的transact()函數介面。
execTransact()函數-- 其角色與transact()函數是相同的,只是這是用來讓C/C++本地程式來調用的。
onTransact()函數-- 這是一個抽象函數,讓應用子類來覆寫(Override)的。上述的transact()和execTransact()兩者都是調用onTransact()函數來實現反向調用(IoC, Inversion of Control)的。
init()函數-- 這是一個本地(Native)函數,讓JNI模組來實現這個函數。Binder()建構函式(Constructor)會調用這個init()本地函數。
這Binder.java是抽象類別,它含有一個抽象)函數:onTransact()。於是,這個軟體主板提供了兩個介面:CI和介面。如下圖:
這是標準型的主板模式。此圖裡的Binder抽象父類和兩個介面,整合起來成為一個典型的軟體主板。如下圖:
這個Binder軟體主板是用來整合兩個進程裡的軟體模組(如類),所以我們稱之為:。如下圖:
基於這個主板,我們就能開始進行組合了。此時,可設計一個子類,並且裝配到主板的介面上。如下圖:
圖 1 Binder處理序間通訊模型
Client和Server均通過函數ioctl與Binder驅動進行資料互動。ioctl是Linux中用於控制I/O裝置的函數,提供了一種同時向裝置發送控制參數和資料的手段。它是一個可變參數的函數,原型為:
<??#65533;"http://www.2cto.com/kf/ware/vc/" target="_blank" class="keylink">vcD4KPHByZSBjbGFzcz0="brush:java;">int ioctl(int fd, int cmd, ...);
fd是開啟/dev/binder裝置後得到的檔案描述符,cmd是對裝置的控制命令。該函數執行成功返回0,否則返回-1。
3.相關資料結構
當函數ioctl的第二個參數cmd為BINDER_WRITE_READ時,表示向Binder驅動發送一條讀取或者寫入/dev/binder裝置的命令,Binder驅動會將對裝置的讀寫“翻譯”為對共用
記憶體區的讀寫。這條命令是Client和Server進行處理序間通訊時最重要、使用最頻繁的控制命令。
傳入BINDER_WRITE_READ的同時,會傳入一個binder_write_read結構體的指標作為ioctl的第三個參數,該結構中的read_buffer和write_buffer欄位分別指向將要讀取或者寫入的緩衝區。這兩個緩衝區中的資料都是以“資料類型+資料內容”的格式順序存放的,而且多條不同類型的資料連續存放,如圖2所示。write_buffer中資料類型以“BC_”開頭,而read_buffer中資料類型以 “BR_”開頭,圖2中以write_buffer中的資料為例。在所有的資料類型中,又以BC(R)_REPLY和BC(R)_TRANSACTION 最為重要:通過BC_TRANSACTION/BC_REPLY這對命令,發送方將資料發往接受方;通過BR_TRANSACTION /BR_REPLY,接收方讀取發送方發來的資料。
資料的內容是一個binder_transaction_data結構。
圖2 Binder IPC中各資料結構的關係
binder_transaction_data結構是對處理序間通訊資料的封裝,可以看作網路通訊中的一個資料包。其中的 sender_uid,sender_pid成員變數指明了此資料發送方的使用者ID和進程ID,buffer成員變數指向處理序間通訊最核心的承載資料,data_size是承載資料的長度。在Binder機制中,sender_uid和sender_pid是在核心中由Binder驅動填入的,無法被偽造,保證了身份標記的可靠性,由此可見Binder處理序間通訊機制進行是安全的。
4.Binder之間的資料行為關係
Client和Server使用Binder機制進行處理序間通訊時,通過分析Client發往Server的資料或者分析Server讀取的Client的請求資料,便可以識別出Client的具體行為。
例如,當Client想要得到定位資訊,請求LocationServer擷取定位元據時,會訪問LocationServer的ILocationManager介面,發往LocationServer中的承載數
據中包含“android.location.ILocationManager”字串。所以我們分析LocationServer讀取的Client發來的請求資料,判斷其中是否包含“android.location.ILocationManager”,
我們就可以知道Client是否正在試圖訪問使用者的地理位置資訊。
由於Android系統中每個應用程式都有自己唯一的UID,因此根據binder_transaction_data中的sender_uid,我們就可以擷取Client具體代表的應用程式。這樣
就獲得了具體軟體的具體行為。
通過對Android系統的分析,我們發現:雖然系統提供的服務多達幾十種,但是實際上只有三個Server進程負責管理
Android系統Server進程與管理的服務
服務進程 管理的服務
com.android.phone 與通訊功能相關的簡訊、電話語音
mediaserver 與媒體功能相關的視頻、音頻服務
system_server 其他服務,如地理位置、藍芽、網路連接、程式安裝卸載等