經過前面幾節的鋪墊,我們對Binder有了個整體認識,這節主要從使用的角度看下本地進程調用遠程服務的流程是怎麼樣的?中間經過了那些步驟,爲什麼我們平常使用遠程服務就像在本進程中一樣,是什麼讓我們模糊了遠程服務在多個進程之間的邊界?答案就是Android的Binder跨進程傳輸機制。
Binder作爲android各種組件的粘合劑,其核心作用毋庸置疑,下面我們就從四大組件的bindService來具體說下調用遠程服務的詳細流程。注意我們提到遠程服務,這裏的服務都是實現了IBinder接口的,準確說是繼承了Binder類的。
在詳細講解之前,先看下客戶端調用AMS的bindService遠程接口的整體流程:
在Activity裏調用bindService時,實際調用的是ContextWrapper類的bindService,其實現如下:
frameworks/base/core/java/android/content/ContextWrapper.java
681 @Override
682 public boolean bindService(Intent service, ServiceConnection conn,
683 int flags) {
684 return mBase.bindService(service, conn, flags);
685 }
這裏調用是mBase的 bindService方法,這裏的mBase其實就是ContextImpl,mBase具體創建是在創建Activity時候生成的,大家有興趣可以看看Activity的啓動流程就知道了,Android的設計還是比較好的,真正的Context的實現是獨立於具體Activity的,這帶來的直接好處就是可以替換Context的實現,說了點題外話,迴歸正題,接着看下ContextImpl的bindService。
frameworks/base/core/java/android/app/ContextImpl.java
1554 public boolean bindService(Intent service, ServiceConnection conn,
1555 int flags) {
1556 warnIfCallingFromSystemProcess();
1557 return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
1558 Process.myUserHandle());
1559 }
接着看bindServiceCommon這個方法。
1597 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
1598 handler, UserHandle user) {
1599 // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
1600 IServiceConnection sd; // (1)
1601 if (conn == null) {
1602 throw new IllegalArgumentException("connection is null");
1603 }
1604 if (mPackageInfo != null) {
1605 sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags); // (2)
1606 } else {
1607 throw new RuntimeException("Not supported in system context");
1608 }
我們只看重點,從上面的bindService我們知道,其有個參數就是ServiceConnection,這個參數就是客戶端用來接收遠程服務代理對象的一個接口而已,由客戶端實現,爲什麼要這樣做,因爲這裏的遠程服務是一個匿名服務,客戶端沒辦法查詢,只能通過bindService的時候經由參數ServiceConnection來被動接收遠程服務代理對象。
所以,從這個角度來看,客戶端提供了ServiceConnection,因此這裏客戶端充當了服務,但是這個ServiceConnection並不是一個IBinder,還不具備遠程通信的能力。我們看下代碼(2),這裏通過LoadApk(代碼中mPackageInfo 就是一個LoadApk類型的變量)對象來生成一個IServiceConnection的IBinder對象,我們的ServiceConnection對象也保存到了LoadApk對象裏了。
接下來看看LoadApk裏的方法getServiceDispatcher
frameworks/base/core/java/android/app/LoadedApk.java
1414 public final IServiceConnection getServiceDispatcher(ServiceConnection c,
1415 Context context, Handler handler, int flags) {
1416 synchronized (mServices) {
1417 LoadedApk.ServiceDispatcher sd = null;
......
1423 if (sd == null) {
1424 sd = new ServiceDispatcher(c, context, handler, flags);
......
1434 return sd.getIServiceConnection();
1435 }
1436 }
我們這裏要說的是Binder,所以直奔主題,從代碼看出IServiceConnection是由LoadApk的內部類ServiceDispatcher的getIServiceConnection()返回的,進入ServiceDispatcher看看。
1489 static final class ServiceDispatcher {
1490 private final ServiceDispatcher.InnerConnection mIServiceConnection;
1491 private final ServiceConnection mConnection; // (1)
......
1506 private static class InnerConnection extends IServiceConnection.Stub { // (2)
1507 final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;
1508
1509 InnerConnection(LoadedApk.ServiceDispatcher sd) {
1510 mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(sd);
1511 }
1512
到這裏明白了,(1)處保存了客戶端實現的ServiceConnection 對象;(2)處InnerConnection 就是客戶端的IBinder實體,其將會被傳到遠程服務端,然後遠程服務端通過它把遠程服務對象設置給客戶端。具體怎麼把遠程服務通過客戶端實現的ServiceConnection 接口設置這裏不會講解,我們只說Binder的調用過程。
好了,接下來回到ContextImpl的bindServiceCommon方法,看下InnerConnection 這個IBinder是怎麼樣通過遠程接口傳給AMS服務的。再看下bindServiceCommon方法。
1597 private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
1598 handler, UserHandle user) {
1599 // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
1600 IServiceConnection sd;
......
1618 int res = ActivityManager.getService().bindService(
1619 mMainThread.getApplicationThread(), getActivityToken(), service,
1620 service.resolveTypeIfNeeded(getContentResolver()),
1621 sd, flags, getOpPackageName(), user.getIdentifier());
......
1630 }
從上面代碼我們看到,獲取的sd對象(其實就是InnerConnection )通過AMS的服務接口被調到遠程SystemServer進程(AMS所在進程),接下來我們主要看下這個傳輸的過程了。其中ActivityManager.getService()返回的是IActivityManager接口,這是由AIDL語言生成,細節我們不去看,但是我們知道返回的IActivityManager接口其實是個代理,前面章節介紹過,其在本地的代理對象就是BpBinder,記下這個就可以,因爲對IActivityManager調用的transact都會轉發給BpBinder對象的transact方法。
在IActivityManager的代理裏面會調用parcel.writeStrongBinder(sd)把InnerConnection壓扁:)其實就是把InnerConnection 轉換到flat_binder_object對象,這個對象前面章節介紹過,記錄了Binder的信息。下面我們看下parcel.writeStrongBinder的實現。JAVA層就不看了,最終會調到native層的Parcel的writeStrongBinder方法。
1081status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
1082{
1083 return flatten_binder(ProcessState::self(), val, this);
1084}
接下來看看flatten_binder方法。
248status_t flatten_binder(const sp<ProcessState>& /*proc*/,
249 const wp<IBinder>& binder, Parcel* out)
250{
251 flat_binder_object obj;
252
253 obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
254 if (binder != NULL) {
255 sp<IBinder> real = binder.promote();
256 if (real != NULL) {
257 IBinder *local = real->localBinder(); // (1)
258 if (!local) {
259 BpBinder *proxy = real->remoteBinder(); // (2)
260 if (proxy == NULL) {
261 ALOGE("null proxy");
262 }
263 const int32_t handle = proxy ? proxy->handle() : 0;
264 obj.type = BINDER_TYPE_WEAK_HANDLE; // (3)
265 obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
266 obj.handle = handle; // (4)
267 obj.cookie = 0;
268 } else {
269 obj.type = BINDER_TYPE_WEAK_BINDER; // (5)
270 obj.binder = reinterpret_cast<uintptr_t>(binder.get_refs());
271 obj.cookie = reinterpret_cast<uintptr_t>(binder.unsafe_get()); // (6)
272 }
273 return finish_flatten_binder(real, obj, out);
274 }
......
295}
這裏我們標註了6處關鍵代碼,下面進行說明。
1)(1)這裏獲取本地IBinder,很顯然,這裏的sd其實就是 InnerConnection,它是從Binder繼承來的(JAVA層的Binder類其實緩存的是本地的JavaBBinderHolder,而JavaBBinderHolder.get返回的對象是JavaBBinder,這其實是一個BBinder,因爲JavaBBinder是BBinder的子類),因此返回的IBinder是不爲空的(再不明白看看前面章節Binder說明),所以,邏輯轉入(5)。
2)(5) 這裏首先把flat_binder_object的type賦值爲BINDER_TYPE_WEAK_BINDER,表示這是一個實體Binder(本地)。
3)(6) 這裏把實體IBinder的對象地址直接賦值給flat_binder_object的cookie成員,注意,這個cookie成員很重要,在驅動把請求轉到客戶端進程的時候會直接把它轉換爲JavaBBinder對象。
到這裏客戶端的對象InnerConnection已經被保存在結構flat_binder_object裏了(其它普通字段這裏就不介紹了),接下來調用IActivityManager遠程接口代理對象BpBinder的transact方法(爲什麼?不明白查看BpBinder介紹),下面看下BpBinder對象的transact方法實現。
frameworks/native/libs/binder/BpBinder.cpp
160status_t BpBinder::transact(
161 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
162{
163 // Once a binder has died, it will never come back to life.
164 if (mAlive) {
165 status_t status = IPCThreadState::self()->transact(
166 mHandle, code, data, reply, flags);
167 if (status == DEAD_OBJECT) mAlive = 0;
168 return status;
169 }
170
171 return DEAD_OBJECT;
172}
BpBinder實際的調用轉到IPCThreadState::self()->transact,繼續查看IPCThreadState的transact方法。
frameworks/native/libs/binder/IPCThreadState.cpp
559status_t IPCThreadState::transact(int32_t handle,
560 uint32_t code, const Parcel& data,
561 Parcel* reply, uint32_t flags)
562{
563 status_t err = data.errorCheck();
......
577 err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); // (1)
578 }
......
593 if (reply) {
594 err = waitForResponse(reply);// (2)
595 } else {
596 Parcel fakeReply;
597 err = waitForResponse(&fakeReply);
598 }
......
618 return err;
619}
首先再強調下IPCThreadState::transact的第一個參數handle,這個handle其實就是IActivityManager遠程服務的句柄(這個很重要,Binder驅動層就是通過這個handle來查詢到是哪個進程提供服務的以及具體的服務,因爲需要把客戶端請求轉到目標進程執行,我們知道這裏的IActivityManager對應的目標進程就是SystemServer)。
在(1)這裏把客戶端的請求做一次封裝,命令碼爲BC_TRANSACTION,進去IPCThreadState::writeTransactionData看看。
914status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
915 int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
916{
917 binder_transaction_data tr;
......
944 mOut.writeInt32(cmd);
945 mOut.write(&tr, sizeof(tr));
946
947 return NO_ERROR;
948}
請求數據封裝在binder_transaction_data結構裏,同時把命令碼和此結構寫入mOut(這也是一個Parcel對象)。緊接着調用IPCThreadState::waitForResponse把客戶端的請求送到Binder的內核驅動。
722status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
723{
......
727 while (1) {
728 if ((err=talkWithDriver()) < NO_ERROR) break;
通過系統調用發送客戶到請求到內核是通過talkWithDriver方法裏調用ioctl實現的,往下看。
813status_t IPCThreadState::talkWithDriver(bool doReceive)
......
861 do {
......
865#if defined(__ANDROID__)
866 if (ioctl(mProcess->mDriverFD, BINDER_WRITE_READ, &bwr) >= 0)
867 err = NO_ERROR;
868 else
869 err = -errno;
終於看到了實質性代碼ioctl,通過此係統調用轉入內核層,命令碼是BINDER_WRITE_READ(讓Binder驅動完成一次寫入和讀取)。現在是由客戶端通過系統調用轉到Binder的內核層,接下來就是Binder驅動乾的事情了。Binder驅動主要完成如下事情:
1)爲IBinder類型的對象生成Binder_node節點信息,同時生成handle保存引用到內核;
2)碰到結構flag_binder_object裏的type是BINDER_TYPE_BINDER類型的,更改爲BINDER_TYPE_HANDLE;
3)通過代理的handle找到服務IBinder實體,把其放到binder_transaction_data結構的target裏;
4)通過代理的handle找到其宿主進程,把請求投送到宿主進程的todo隊列,喚醒對應的宿主進程的binder線程執行;
這裏要特別注意,因爲Binder驅動實現mmap系統調用,其會把內核地址映射到用戶進程地址空間,因此當內核往這個地址空間寫入數據時,在用戶空間會同時反應出來,這也是爲什麼Binder機制只實現一次copy的原因所在。
經過Binder驅動層處理,流程轉到宿主進程,這裏就是SystemServer進程,前面我們講過每個進程都會啓動Binder進程來處理客戶端發來的請求,最後邏輯由IPCThreadState::executeCommand執行。
957status_t IPCThreadState::executeCommand(int32_t cmd)
958{
959 BBinder* obj;
960 RefBase::weakref_type* refs;
......
1036 case BR_TRANSACTION:
1037 {
1038 binder_transaction_data tr;
1039 result = mIn.read(&tr, sizeof(tr));
......
1075 if (tr.target.ptr) {
......
1078 if (reinterpret_cast<RefBase::weakref_type*>(
1079 tr.target.ptr)->attemptIncStrong(this)) {
1080 error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
1081 &reply, tr.flags);
1082 reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
最終會調用BBinder的transact方法,而BBinder最終會調用虛方法onTransact。
118status_t BBinder::transact(
119 uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
120{
......
124 switch (code) {
125 case PING_TRANSACTION:
......
128 default:
129 err = onTransact(code, data, reply, flags);
130 break;
131 }
132......
138}
我們知道這裏的BBinder其實就是JavaBBinder,從上節我們知道,其會調用JAVA層服務對象的onTransact,這樣處理轉到實際的服務去執行了,這裏的服務就是IActivieyManager,在SystemServer進程裏實現類就是ActivityManagerService。
到這裏就完成了一次Binder請求。好吧,如果還不明白的話,大家可以回到開頭看看整體調用流程圖再結合源碼就會明白了,這裏大家要重點注意Parce類的writeStrongBinder和readStrongBinder這兩個方法,它們其實就是把Binder對象(本地或遠程)壓扁成flat_binder_object結構,因爲在Binder驅動層這個結構其實就代表了一個IBinder對象,內核會爲它生成一個handle,而客戶端持有的遠程代理對象實質上就是持有實體IBinder對應的handler而已(這個handle有本地對象BpBinder來封裝)。
最後還得提下IBinder對象就是通過flat_binder_object這個結構經由Binder驅動層實現了IBinder對象在進程之間傳遞,這給調用者造成一個假象,感覺遠程對象就真的能在進程間傳遞,事實當然不是這樣,因爲任何IBinder對象實體經由Binder驅動層處理後都變成了對IBinder對象的句柄handle的持有,前面已經介紹過,實體IBinder在flat_binder_object結構裏的的type變量值是BINDER_TYPE_BINDER或BINDER_TYPE_WEAK_BINDER,其cookie就是實體IBinder對象地址,其handle變量是0(這時還沒有經過Binder驅動處理,其對應的handle可能還沒有生成),當通過ioctl把這些數據傳遞給Binder驅動後,Binder驅動會對所有的IBinder對象進行轉換處理(生成IBinder的handle,在驅動層插入節點binder_node記錄下這個IBinder對象)。
大家可以看下驅動層源碼,對這部分描述就會明白了,隨便提一點,Parcel裏記錄了所有的IBinder對象在內存中的偏移位置和IBinder對象的個數,在通過命令碼BC_TRANSACTION把數據格式封裝爲binder_transaction_data結構時,這個結構裏同時也記錄了IBinder的信息(對象偏移和字節數),看下IPCThreadState::writeTransactionData代碼片段就明白了。
914status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags,
915 int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer)
916{
917 binder_transaction_data tr;
918
919 tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */
920 tr.target.handle = handle;
921 tr.code = code;
922 tr.flags = binderFlags;
923 tr.cookie = 0;
924 tr.sender_pid = 0;
925 tr.sender_euid = 0;
926
927 const status_t err = data.errorCheck();
928 if (err == NO_ERROR) {
929 tr.data_size = data.ipcDataSize();
930 tr.data.ptr.buffer = data.ipcData();
931 tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t);
932 tr.data.ptr.offsets = data.ipcObjects();
933 } else if (statusBuffer) {
......
944 mOut.writeInt32(cmd);
945 mOut.write(&tr, sizeof(tr));
946
947 return NO_ERROR;
948}
上面標紅的代碼段就是記錄了IBinder對象的信息。
本系列文章均爲原創,主要總結作者多年在軟件行業的一些經驗,和大家共同學習、進步,轉載請註明出處,謝謝!