上一篇文章中我們討論了如何向系統註冊Service。本篇文章我們將討論如何使用這個登入的系統Service。
在本系列文章的第一篇中,用戶端應用程式使用如下兩條語句取得了ExampleService代理對象的引用。
sp<IServiceManager> sm = defaultServiceManager();<br />binder = sm->getService(String16("byn.example"));
第一句我們之前已經詳細解釋過了,全域函數defaultServiceManager()返回的是ServiceManager代理對象的引用。第二句話以ExampleService的方法名為參數調用getService()方法,返回的是ExampleService代理對象的引用。這個過程和上一篇文章介紹的addService的過程是非常類似的,這裡就不詳細展開了。
客戶應用程式獲得了ExampleService代理對象的引用之後,通過如下語句調用服務:
data.writeInt32(getpid());<br />data.writeInt32(n);<br />binder->transact(0, data, &reply);
第一句話寫入當前進程的進程ID,這裡只是為了輸出log用,沒有實際意義;
第二句話寫入參數n;
最後一句話調用ExampleService代理對象的transact方法發送請求。第一個參數0是請求代碼(code),如果一個服務提供了多個API介面,那麼伺服器端就通過這個參數區分調用的是哪一個API;第二個參數打包後的調用參數;最後一個參數儲存傳回值。因為ExampleService代理對象繼承自BpBinder,所以這裡調用的是BpBinder::transact()方法,進而調用IPCThreadState::transact()方法。這個過程在上一篇文章中已經介紹過。
現在我們重點剖析一下伺服器端的情況。
伺服器端在向系統註冊服務之後,首先調用ProcessState::self()->startThreadPool()方法啟動一個線程池;然後調用IPCThreadState::self()->joinThreadPool()方法進入一個無限迴圈,等待其它進程的服務要求。我們看一下joinThreadPool方法的原始碼:
// File: frameworks/base/libs/binder/IPCThreadState.cpp<br />void IPCThreadState::joinThreadPool(bool isMain)<br />{<br /> LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL/n", (void*)pthread_self(), getpid());</p><p> mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);</p><p> status_t result;<br /> do {<br /> int32_t cmd;</p><p> // When we've cleared the incoming command queue, process any pending derefs<br /> if (mIn.dataPosition() >= mIn.dataSize()) {<br /> size_t numPending = mPendingWeakDerefs.size();<br /> if (numPending > 0) {<br /> for (size_t i = 0; i < numPending; i++) {<br /> RefBase::weakref_type* refs = mPendingWeakDerefs[i];<br /> refs->decWeak(mProcess.get());<br /> }<br /> mPendingWeakDerefs.clear();<br /> }</p><p> numPending = mPendingStrongDerefs.size();<br /> if (numPending > 0) {<br /> for (size_t i = 0; i < numPending; i++) {<br /> BBinder* obj = mPendingStrongDerefs[i];<br /> obj->decStrong(mProcess.get());<br /> }<br /> mPendingStrongDerefs.clear();<br /> }<br /> }</p><p> // now get the next command to be processed, waiting if necessary<br /> result = talkWithDriver();<br /> if (result >= NO_ERROR) {<br /> size_t IN = mIn.dataAvail();<br /> if (IN < sizeof(int32_t)) continue;<br /> cmd = mIn.readInt32();<br /> IF_LOG_COMMANDS() {<br /> alog << "Processing top-level Command: "<br /> << getReturnString(cmd) << endl;<br /> }</p><p> result = executeCommand(cmd);<br /> }</p><p> // After executing the command, ensure that the thread is returned to the<br /> // default cgroup and priority before rejoining the pool. This is a failsafe<br /> // in case the command implementation failed to properly restore the thread's<br /> // scheduling parameters upon completion.<br /> int my_id;<br />#ifdef HAVE_GETTID<br /> my_id = gettid();<br />#else<br /> my_id = getpid();<br />#endif<br /> if (!set_sched_policy(my_id, SP_FOREGROUND)) {<br /> // success; reset the priority as well<br /> setpriority(PRIO_PROCESS, my_id, ANDROID_PRIORITY_NORMAL);<br /> }</p><p> // Let this thread exit the thread pool if it is no longer<br /> // needed and it is not the main process thread.<br /> if(result == TIMED_OUT && !isMain) {<br /> break;<br /> }<br /> } while (result != -ECONNREFUSED && result != -EBADF);</p><p> LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%p/n",<br /> (void*)pthread_self(), getpid(), (void*)result);</p><p> mOut.writeInt32(BC_EXIT_LOOPER);<br /> talkWithDriver(false);<br />}
在joinThreadPool()方法中,通過調用talkWithDriver方法與binder裝置進行通訊,通常Service進程會阻塞在這裡;一旦客戶請求到來,該方法返回,並調用後面的executeCommand()方法進行處理。我們看一下executeCommand()方法的原始碼:
// File: frameworks/base/libs/binder/IPCThreadState.cpp<br />status_t IPCThreadState::executeCommand(int32_t cmd)<br />{<br /> BBinder* obj;<br /> RefBase::weakref_type* refs;<br /> status_t result = NO_ERROR;</p><p> switch (cmd) {<br /> case BR_ERROR:<br /> result = mIn.readInt32();<br /> break;</p><p> case BR_OK:<br /> break;</p><p> case BR_ACQUIRE:<br /> refs = (RefBase::weakref_type*)mIn.readInt32();<br /> obj = (BBinder*)mIn.readInt32();<br /> LOG_ASSERT(refs->refBase() == obj,<br /> "BR_ACQUIRE: object %p does not match cookie %p (expected %p)",<br /> refs, obj, refs->refBase());<br /> obj->incStrong(mProcess.get());<br /> IF_LOG_REMOTEREFS() {<br /> LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj);<br /> obj->printRefs();<br /> }<br /> mOut.writeInt32(BC_ACQUIRE_DONE);<br /> mOut.writeInt32((int32_t)refs);<br /> mOut.writeInt32((int32_t)obj);<br /> break;</p><p> case BR_RELEASE:<br /> refs = (RefBase::weakref_type*)mIn.readInt32();<br /> obj = (BBinder*)mIn.readInt32();<br /> LOG_ASSERT(refs->refBase() == obj,<br /> "BR_RELEASE: object %p does not match cookie %p (expected %p)",<br /> refs, obj, refs->refBase());<br /> IF_LOG_REMOTEREFS() {<br /> LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj);<br /> obj->printRefs();<br /> }<br /> mPendingStrongDerefs.push(obj);<br /> break;</p><p> case BR_INCREFS:<br /> refs = (RefBase::weakref_type*)mIn.readInt32();<br /> obj = (BBinder*)mIn.readInt32();<br /> refs->incWeak(mProcess.get());<br /> mOut.writeInt32(BC_INCREFS_DONE);<br /> mOut.writeInt32((int32_t)refs);<br /> mOut.writeInt32((int32_t)obj);<br /> break;</p><p> case BR_DECREFS:<br /> refs = (RefBase::weakref_type*)mIn.readInt32();<br /> obj = (BBinder*)mIn.readInt32();<br /> // NOTE: This assertion is not valid, because the object may no<br /> // longer exist (thus the (BBinder*)cast above resulting in a different<br /> // memory address).<br /> //LOG_ASSERT(refs->refBase() == obj,<br /> // "BR_DECREFS: object %p does not match cookie %p (expected %p)",<br /> // refs, obj, refs->refBase());<br /> mPendingWeakDerefs.push(refs);<br /> break;</p><p> case BR_ATTEMPT_ACQUIRE:<br /> refs = (RefBase::weakref_type*)mIn.readInt32();<br /> obj = (BBinder*)mIn.readInt32();</p><p> {<br /> const bool success = refs->attemptIncStrong(mProcess.get());<br /> LOG_ASSERT(success && refs->refBase() == obj,<br /> "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)",<br /> refs, obj, refs->refBase());</p><p> mOut.writeInt32(BC_ACQUIRE_RESULT);<br /> mOut.writeInt32((int32_t)success);<br /> }<br /> break;</p><p> case BR_TRANSACTION:<br /> {<br /> binder_transaction_data tr;<br /> result = mIn.read(&tr, sizeof(tr));<br /> LOG_ASSERT(result == NO_ERROR,<br /> "Not enough command data for brTRANSACTION");<br /> if (result != NO_ERROR) break;</p><p> Parcel buffer;<br /> buffer.ipcSetDataReference(<br /> reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),<br /> tr.data_size,<br /> reinterpret_cast<const size_t*>(tr.data.ptr.offsets),<br /> tr.offsets_size/sizeof(size_t), freeBuffer, this);</p><p> const pid_t origPid = mCallingPid;<br /> const uid_t origUid = mCallingUid;</p><p> mCallingPid = tr.sender_pid;<br /> mCallingUid = tr.sender_euid;</p><p> //LOGI(">>>> TRANSACT from pid %d uid %d/n", mCallingPid, mCallingUid);</p><p> Parcel reply;<br /> IF_LOG_TRANSACTIONS() {<br /> TextOutput::Bundle _b(alog);<br /> alog << "BR_TRANSACTION thr " << (void*)pthread_self()<br /> << " / obj " << tr.target.ptr << " / code "<br /> << TypeCode(tr.code) << ": " << indent << buffer<br /> << dedent << endl<br /> << "Data addr = "<br /> << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)<br /> << ", offsets addr="<br /> << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;<br /> }<br /> if (tr.target.ptr) {<br /> sp<BBinder> b((BBinder*)tr.cookie);<br /> const status_t error = b->transact(tr.code, buffer, &reply, 0);<br /> if (error < NO_ERROR) reply.setError(error);</p><p> } else {<br /> const status_t error = the_context_object->transact(tr.code, buffer, &reply, 0);<br /> if (error < NO_ERROR) reply.setError(error);<br /> }</p><p> //LOGI("<<<< TRANSACT from pid %d restore pid %d uid %d/n",<br /> // mCallingPid, origPid, origUid);</p><p> if ((tr.flags & TF_ONE_WAY) == 0) {<br /> LOG_ONEWAY("Sending reply to %d!", mCallingPid);<br /> sendReply(reply, 0);<br /> } else {<br /> LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);<br /> }</p><p> mCallingPid = origPid;<br /> mCallingUid = origUid;</p><p> IF_LOG_TRANSACTIONS() {<br /> TextOutput::Bundle _b(alog);<br /> alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "<br /> << tr.target.ptr << ": " << indent << reply << dedent << endl;<br /> }</p><p> }<br /> break;</p><p> case BR_DEAD_BINDER:<br /> {<br /> BpBinder *proxy = (BpBinder*)mIn.readInt32();<br /> proxy->sendObituary();<br /> mOut.writeInt32(BC_DEAD_BINDER_DONE);<br /> mOut.writeInt32((int32_t)proxy);<br /> } break;</p><p> case BR_CLEAR_DEATH_NOTIFICATION_DONE:<br /> {<br /> BpBinder *proxy = (BpBinder*)mIn.readInt32();<br /> proxy->getWeakRefs()->decWeak(proxy);<br /> } break;</p><p> case BR_FINISHED:<br /> result = TIMED_OUT;<br /> break;</p><p> case BR_NOOP:<br /> break;</p><p> case BR_SPAWN_LOOPER:<br /> mProcess->spawnPooledThread(false);<br /> break;</p><p> default:<br /> printf("*** BAD COMMAND %d received from Binder driver/n", cmd);<br /> result = UNKNOWN_ERROR;<br /> break;<br /> }</p><p> if (result != NO_ERROR) {<br /> mLastError = result;<br /> }</p><p> return result;<br />}
這裡,函數會根據一系列枚舉值作相應的處理。在binder協議中:
BR_XXX等宏為BinderDriverReturnProtocol,表示Binder驅動返回協議。
BC_XXX等宏為BinderDriverCommandProtocol,表示Binder驅動命令協議。
因為這裡是收到命令請求後要做相應的處理,所以這裡的宏都是以BR開頭的。這裡會走到BR_TRANSACTION這個分支,調用BBinder的transact()方法做處理(b->transact(tr.code, buffer, &reply, 0))。我們看一下BBinder::transact()方法的原始碼:
// File: frameworks/base/libs/binder/binder.cpp<br />status_t BBinder::transact(<br /> uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)<br />{<br /> data.setDataPosition(0);</p><p> status_t err = NO_ERROR;<br /> switch (code) {<br /> case PING_TRANSACTION:<br /> reply->writeInt32(pingBinder());<br /> break;<br /> default:<br /> err = onTransact(code, data, reply, flags);<br /> break;<br /> }</p><p> if (reply != NULL) {<br /> reply->setDataPosition(0);<br /> }</p><p> return err;<br />}
我們看到調用的是虛函數onTransact()。因為ExampleService類繼承自BBinder類,並改寫了onTransact()方法,所以這裡會調用到ExampleService::onTransact()方法。
status_t ExampleService::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)<br /> {<br /> switch(code)<br /> {<br /> case 0: {<br /> pid_t pid = data.readInt32();<br /> int num = data.readInt32();<br /> num = num + 100;<br /> reply->writeInt32(num);<br /> return NO_ERROR;<br /> }<br /> break;<br /> default:<br /> return BBinder::onTransact(code, data, reply, flags);<br /> }<br /> }
看到這裡首先根據請求代碼作相應的處理。還記得我們發送請求時用的是代碼0,所以這裡會走到"case0"這個分支。程式先順序讀出兩個參數:進程ID和被加數,將被加數加上100之後返回。至此,伺服器端完成了用戶端的服務要求。
我們將總共四篇文章涉及到的主要類用下面的類圖作一總結:
說明:
1. Android系統使用binder機制實現處理序間通訊(IPC),這裡主要涉及到以下幾個類:
1.1 IBinder是Android系統對binder機制的抽象,任何一個向系統註冊的Service都必須繼承IBinder介面(如:ExampleService繼承BBinder,而BBinder繼承IBinder)
1.2 IInterface我們在這一系列文章裡沒有過多涉及。它的目的是進一步抽象binder機制。比如要使用我們的ExampleService,用戶端應用程式必須顯式調用IPCThreadState::transaction()方法,對使用者來說還是不太友好。如果我們定義一個新的類IExampleServiceInterface繼承Interface,在這個類中定義add100()介面,ExampleService的代理對象也擁有該介面,那麼用戶端應用程式直接調用代理對象的add100()方法就好了,這樣做對使用者更友好。比如ServiceManager就是這樣實現的(IServiceManager繼承IInterface)。用戶端調用的是addService介面而不是transaction方法。
1.3 ProcessState類是一個singleton類型,每個進程只能建立一個執行個體,它的作用是管理當前進程中的所有Service代理對象(BpBinder對象)。任何一個使用binder機制的進程都必須建立一個該類的執行個體。
1.4 IPCThreadState類是processState類的友元類,它的作用是封裝對binder裝置的I/O操作。用戶端通過調用它的transact()方法完成發送請求;伺服器端調用他的joinThreadState()方法等待用戶端的服務要求。
2. Android的binder機制本質上是Proxy模式的一個具體實現。
3. ServiceManager是整個Android系統的Service管理員,任何一個系統Service首先要向它註冊才能提供服務。註冊時,首先要獲得它的代理對象(BpServiceManager),然後通過調用它的addService()方法完成註冊。用戶端通過調用它的getService()擷取系統服務的代理兌現。ServiceManager在系統中始終對應控制代碼0。
4. 用戶端通過調用IPCThreadState的transaction方法發送請求;伺服器端通過改寫BBinder的onTransaction()方法實現接受請求。
(全文完)