編輯:Android資訊
本文是Android Binder機制解析的第二篇文章,會講解Binder Framework的C++部分邏輯。
Binder機制分析的第一篇文章,請移步這裡:理解 Android Binder 機制(一):驅動篇
Framework是一個中間層,它對接了底層實現,封裝了復雜的內部邏輯,並提供供外部使用的接口。Framework層是應用程序開發的基礎。
Binder Framework層分為C++和Java兩個部分,為了達到功能的復用,中間通過JNI進行銜接。
Binder Framework的C++部分,頭文件位於這個路徑:/frameworks/native/include/binder/,實現位於這個路徑:/frameworks/native/libs/binder/ 。Binder庫最終會編譯成一個動態鏈接庫:libbinder.so
,供其他進程鏈接使用。
為了便於說明,下文中我們將Binder Framework 的C++部分稱之為libbinder。
libbinder中,將實現分為Proxy和Native兩端。Proxy對應了上文提到的Client端,是服務對外提供的接口。而Native是服務實現的一端,對應了上文提到的Server端。類名中帶有小寫字母p的(例如BpInterface),就是指Proxy端。類名帶有小寫字母n的(例如BnInterface),就是指Native端。
Proxy代表了調用方,通常與服務的實現不在同一個進程,因此下文中,我們也稱Proxy端為“遠程”端。Native端是服務實現的自身,因此下文中,我們也稱Native端為”本地“端。
這裡,我們先對libbinder中的主要類做一個簡要說明,了解一下它們的關系,然後再詳細的講解。
下圖描述了這些類之間的關系:
另外說明一下,Binder服務的實現類(圖中紫色部分)通常都會遵守下面的命名規則:
看了上面這些介紹,你可能還是不太容易理解。不過不要緊,下面我們會逐步拆分講解這些內容。
在這幅圖中,淺黃色部分的結構是最難理解的,因此我們先從它們著手。
我們先來看看IBinder這個類。這個類描述了所有在Binder上傳遞的對象,它既是Binder本地對象BBinder的父類,也是Binder遠程對象BpBinder的父類。這個類中的主要方法說明如下:
BpBinder的實例代表了遠程Binder,這個類的對象將被客戶端調用。其中handle
方法會返回指向Binder服務實現者的句柄,這個類最重要就是提供了transact
方法,這個方法會將遠程調用的參數封裝好發送的Binder驅動。
由於每個Binder服務通常都會提供多個服務接口,而這個方法中的uint32_t code
參數就是用來對服務接口進行編號區分的。Binder服務的每個接口都需要指定一個唯一的code,這個code要在Proxy和Native端配對好。當客戶端將請求發送到服務端的時候,服務端根據這個code(onTransact方法中)來區分調用哪個接口方法。
BBinder的實例代表了本地Binder,它描述了服務的提供方,所有Binder服務的實現者都要繼承這個類(的子類),在繼承類中,最重要的就是實現onTransact
方法,因為這個方法是所有請求的入口。因此,這個方法是和BpBinder中的transact
方法對應的,這個方法同樣也有一個uint32_t code
參數,在這個方法的實現中,由服務提供者通過code對請求的接口進行區分,然後調用具體實現服務的方法。
IBinder中定義了uint32_t code
允許的范圍:
FIRST_CALL_TRANSACTION = 0x00000001, LAST_CALL_TRANSACTION = 0x00ffffff,
Binder服務要保證自己提供的每個服務接口有一個唯一的code,例如某個Binder服務可以將:add接口code設為1,minus接口code設為2,multiple接口code設為3,divide接口code設為4,等等。
講完了IBinder,BpBinder和BBinder三個類,我們再來看看BpReBase,IInterface,BpInterface和BnInterface。
每個Binder服務都是為了某個功能而實現的,因此其本身會定義一套接口集(通常是C++的一個類)來描述自己提供的所有功能。而Binder服務既有自身實現服務的類,也要有給客戶端進程調用的類。為了便於開發,這兩中類裡面的服務接口應當是一致的,例如:假設服務實現方提供了一個接口為add(int a, int b)
的服務方法,那麼其遠程接口中也應當有一個add(int a, int b)
方法。因此為了實現方便,本地實現類和遠程接口類需要有一個公共的描述服務接口的基類(即上圖中的IXXXService)來繼承。而這個基類通常是IInterface的子類,IInterface的定義如下:
class IInterface : public virtual RefBase { public: IInterface(); static sp<IBinder> asBinder(const IInterface*); static sp<IBinder> asBinder(const sp<IInterface>&); protected: virtual ~IInterface(); virtual IBinder* onAsBinder() = 0; };
之所以要繼承自IInterface類是因為這個類中定義了onAsBinder
讓子類實現。onAsBinder
在本地對象的實現類中返回的是本地對象,在遠程對象的實現類中返回的是遠程對象。onAsBinder
方法被兩個靜態方法asBinder
方法調用。有了這些接口之後,在代碼中便可以直接通過IXXX::asBinder
方法獲取到不用區分本地還是遠程的IBinder對象。這個在跨進程傳遞Binder對象的時候有很大的作用(因為不用區分具體細節,只要直接調用和傳遞就好)。
下面,我們來看一下BpInterface和BnInterface的定義:
template<typename INTERFACE> class BnInterface : public INTERFACE, public BBinder { public: virtual sp<IInterface> queryLocalInterface(const String16& _descriptor); virtual const String16& getInterfaceDescriptor() const; protected: virtual IBinder* onAsBinder(); }; // ---------------------------------------------------------------------- template<typename INTERFACE> class BpInterface : public INTERFACE, public BpRefBase { public: BpInterface(const sp<IBinder>& remote); protected: virtual IBinder* onAsBinder(); };
這兩個類都是模板類,它們在繼承自INTERFACE的基礎上各自繼承了另外一個類。這裡的INTERFACE便是我們Binder服務接口的基類。另外,BnInterface繼承了BBinder類,由此可以通過復寫onTransact
方法來提供實現。BpInterface繼承了BpRefBase,通過這個類的remote
方法可以獲取到指向服務實現方的句柄。在客戶端接口的實現類中,每個接口在組裝好參數之後,都會調用remote()->transact
來發送請求,而這裡其實就是調用的BpBinder的transact方法,這樣請求便通過Binder到達了服務實現方的onTransact中。這個過程如下圖所示:
基於Binder框架開發的服務,除了滿足上文提到的類名規則之外,還需要遵守其他一些共同的規約:
getInterfaceDescriptor
返回,類型是一個字符串。通常,Binder服務會在類中定義static const android::String16 descriptor;
這樣一個常量來描述這個標識符,然後在getInterfaceDescriptor
方法中返回這個常量。android::sp<IXXX> asInterface
方法來返回基類對象指針。由於上面提到的這兩點對於所有Binder服務的實現邏輯都是類似的。為了簡化開發者的重復工作,在libbinder中,定義了兩個宏來簡化這些重復工作,它們是:
#define DECLARE_META_INTERFACE(INTERFACE) \ static const android::String16 descriptor; \ static android::sp<I##INTERFACE> asInterface( \ const android::sp<android::IBinder>& obj); \ virtual const android::String16& getInterfaceDescriptor() const; \ I##INTERFACE(); \ virtual ~I##INTERFACE(); \ #define IMPLEMENT_META_INTERFACE(INTERFACE, NAME) \ const android::String16 I##INTERFACE::descriptor(NAME); \ const android::String16& \ I##INTERFACE::getInterfaceDescriptor() const { \ return I##INTERFACE::descriptor; \ } \ android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ const android::sp<android::IBinder>& obj) \ { \ android::sp<I##INTERFACE> intr; \ if (obj != NULL) { \ intr = static_cast<I##INTERFACE*>( \ obj->queryLocalInterface( \ I##INTERFACE::descriptor).get()); \ if (intr == NULL) { \ intr = new Bp##INTERFACE(obj); \ } \ } \ return intr; \ } \ I##INTERFACE::I##INTERFACE() { } \ I##INTERFACE::~I##INTERFACE() { } \
有了這兩個宏之後,開發者只要在接口基類(IXXX)頭文件中,使用DECLARE_META_INTERFACE
宏便完成了需要的組件的聲明。然後在cpp文件中使用IMPLEMENT_META_INTERFACE
便完成了這些組件的實現。
在講解Binder驅動的時候我們就提到:任何使用Binder機制的進程都必須要對/dev/binder
設備進行open
以及mmap
之後才能使用,這部分邏輯是所有使用Binder機制進程共同的。對於這種共同邏輯的封裝便是Framework層的職責之一。libbinder中,ProcessState類封裝了這個邏輯,相關代碼見下文。
這裡是ProcessState構造函數,在這個函數中,初始化mDriverFD的時候調用了open_driver
方法打開binder設備,然後又在函數體中,通過mmap進行內存映射。
ProcessState::ProcessState() : mDriverFD(open_driver()) , mVMStart(MAP_FAILED) , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER) , mThreadCountDecrement(PTHREAD_COND_INITIALIZER) , mExecutingThreadsCount(0) , mMaxThreads(DEFAULT_MAX_BINDER_THREADS) , mStarvationStartTimeMs(0) , mManagesContexts(false) , mBinderContextCheckFunc(NULL) , mBinderContextUserData(NULL) , mThreadPoolStarted(false) , mThreadPoolSeq(1) { if (mDriverFD >= 0) { mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0); if (mVMStart == MAP_FAILED) { // *sigh* ALOGE("Using /dev/binder failed: unable to mmap transaction memory.\n"); close(mDriverFD); mDriverFD = -1; } } LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver could not be opened. Terminating."); }
open_driver
的函數實現如下所示。在這個函數中完成了三個工作:
open
系統調用打開了dev/binder
設備關於這部分邏輯背後的處理,在講解Binder驅動的時候,我們已經講解過了。
static int open_driver() { int fd = open("/dev/binder", O_RDWR | O_CLOEXEC); if (fd >= 0) { int vers = 0; status_t result = ioctl(fd, BINDER_VERSION, &vers); if (result == -1) { ALOGE("Binder ioctl to obtain version failed: %s", strerror(errno)); close(fd); fd = -1; } if (result != 0 || vers != BINDER_CURRENT_PROTOCOL_VERSION) { ALOGE("Binder driver protocol does not match user space protocol!"); close(fd); fd = -1; } size_t maxThreads = DEFAULT_MAX_BINDER_THREADS; result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads); if (result == -1) { ALOGE("Binder ioctl to set max threads failed: %s", strerror(errno)); } } else { ALOGW("Opening '/dev/binder' failed: %s\n", strerror(errno)); } return fd; }
ProcessState
是一個Singleton(單例)類型的類,在一個進程中,只會存在一個實例。通過ProcessState::self()
接口獲取這個實例。一旦獲取這個實例,便會執行其構造函數,由此完成了對於Binder設備的初始化工作。
由於Binder的數據需要跨進程傳遞,並且還需要在內核上開辟空間,因此允許在Binder上傳遞的數據並不是無無限大的。mmap中指定的大小便是對數據傳遞的大小限制:
#define BINDER_VM_SIZE ((1*1024*1024) - (4096 *2)) // 1M - 8k mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
這裡我們看到,在進行mmap的時候,指定了最大size為BINDER_VM_SIZE,即 1M – 8k的大小。 因此我們在開發過程中,一次Binder調用的數據總和不能超過這個大小。
對於這個區域的大小,我們也可以在設備上進行確認。這裡我們還之前提到的system_server為例。上面我們講解了通過procfs來獲取映射的內存地址,除此之外,我們也可以通過showmap命令,來確定這塊區域的大小,相關命令如下:
angler:/ # ps | grep system_server system 1889 526 2353404 135968 SyS_epoll_ 72972eeaf4 S system_server angler:/ # showmap 1889 | grep "/dev/binder" 1016 4 4 0 0 4 0 0 1 /dev/binder
這裡可以看到,這塊區域的大小正是 1M – 8K = 1016k。
Tips: 通過showmap命令可以看到進程的詳細內存占用情況。在實際的開發過程中,當我們要對某個進程做內存占用分析的時候,這個命令是相當有用的。建議讀者嘗試通過showmap命令查看system_server或其他感興趣進程的完整map,看看這些進程都依賴了哪些庫或者模塊,以及內存占用情況是怎樣的。
上文提到ProcessState
是一個單例類,一個進程只有一個實例。而負責與Binder驅動通信的IPCThreadState
也是一個單例類。但這個類不是一個進程只有一個實例,而是一個線程有一個實例。
IPCThreadState
負責了與驅動通信的細節處理。這個類中的關鍵幾個方法說明如下:
BpBinder::transact
方法在發送請求的時候,其實就是直接調用了IPCThreadState
對應的方法來發送請求到Binder驅動的,相關代碼如下:
status_t BpBinder::transact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { if (mAlive) { status_t status = IPCThreadState::self()->transact( mHandle, code, data, reply, flags); if (status == DEAD_OBJECT) mAlive = 0; return status; } return DEAD_OBJECT; }
而IPCThreadState::transact
方法主要邏輯如下:
status_t IPCThreadState::transact(int32_t handle, uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { status_t err = data.errorCheck(); flags |= TF_ACCEPT_FDS; if (err == NO_ERROR) { err = writeTransactionData(BC_TRANSACTION, flags, handle, code, data, NULL); } if (err != NO_ERROR) { if (reply) reply->setError(err); return (mLastError = err); } if ((flags & TF_ONE_WAY) == 0) { if (reply) { err = waitForResponse(reply); } else { Parcel fakeReply; err = waitForResponse(&fakeReply); } } else { err = waitForResponse(NULL, NULL); } return err; }
這段代碼應該還是比較好理解的:首先通過writeTransactionData
寫入數據,然後通過waitForResponse
等待返回結果。TF_ONE_WAY
表示此次請求是單向的,即:不用真正等待結果即可返回。
而writeTransactionData
方法其實就是在組裝binder_transaction_data
數據:
status_t IPCThreadState::writeTransactionData(int32_t cmd, uint32_t binderFlags, int32_t handle, uint32_t code, const Parcel& data, status_t* statusBuffer) { binder_transaction_data tr; tr.target.ptr = 0; /* Don't pass uninitialized stack data to a remote process */ tr.target.handle = handle; tr.code = code; tr.flags = binderFlags; tr.cookie = 0; tr.sender_pid = 0; tr.sender_euid = 0; const status_t err = data.errorCheck(); if (err == NO_ERROR) { tr.data_size = data.ipcDataSize(); tr.data.ptr.buffer = data.ipcData(); tr.offsets_size = data.ipcObjectsCount()*sizeof(binder_size_t); tr.data.ptr.offsets = data.ipcObjects(); } else if (statusBuffer) { tr.flags |= TF_STATUS_CODE; *statusBuffer = err; tr.data_size = sizeof(status_t); tr.data.ptr.buffer = reinterpret_cast<uintptr_t>(statusBuffer); tr.offsets_size = 0; tr.data.ptr.offsets = 0; } else { return (mLastError = err); } mOut.writeInt32(cmd); mOut.write(&tr, sizeof(tr)); return NO_ERROR; }
對於binder_transaction_data
在講解Binder驅動的時候我們已經詳細講解過了。而這裡的Parcel我們還不了解,那麼接下來我們馬上就來看一下這個類。
Binder上提供的是跨進程的服務,每個服務包含了不同的接口,每個接口的參數數量和類型都不一樣。那麼當客戶端想要調用服務端的接口,參數是如何跨進程傳遞給服務端的呢?除此之外,服務端想要給客戶端返回結果,結果又是如何傳遞回來的呢?
這些問題的答案就是:Parcel。Parcel就像一個包裝器,調用者可以以任意順序往裡面放入需要的數據,所有寫入的數據就像是被打成一個整體的包,然後可以直接在Binde上傳輸。
Parcel提供了所有基本類型的寫入和讀出接口,下面是其中的一部分:
... status_t writeInt32(int32_t val); status_t writeUint32(uint32_t val); status_t writeInt64(int64_t val); status_t writeUint64(uint64_t val); status_t writeFloat(float val); status_t writeDouble(double val); status_t writeCString(const char* str); status_t writeString8(const String8& str); status_t readInt32(int32_t *pArg) const; uint32_t readUint32() const; status_t readUint32(uint32_t *pArg) const; int64_t readInt64() const; status_t readInt64(int64_t *pArg) const; uint64_t readUint64() const; status_t readUint64(uint64_t *pArg) const; float readFloat() const; status_t readFloat(float *pArg) const; double readDouble() const; status_t readDouble(double *pArg) const; intptr_t readIntPtr() const; status_t readIntPtr(intptr_t *pArg) const; bool readBool() const; status_t readBool(bool *pArg) const; char16_t readChar() const; status_t readChar(char16_t *pArg) const; int8_t readByte() const; status_t readByte(int8_t *pArg) const; // Read a UTF16 encoded string, convert to UTF8 status_t readUtf8FromUtf16(std::string* str) const; status_t readUtf8FromUtf16(std::unique_ptr<std::string>* str) const; const char* readCString() const; ...
因此對於基本類型,開發者可以直接調用接口寫入和讀出。而對於非基本類型,需要由開發者將其拆分成基本類型然後寫入到Parcel中(讀出的時候也是一樣)。 Parcel會將所有寫入的數據進行打包,Parcel本身可以作為一個整體在進程間傳遞。接收方在收到Parcel之後,只要按寫入同樣的順序讀出即可。
這個過程,和我們現實生活中寄送包裹做法是一樣的:我們將需要寄送的包裹放到硬紙盒中交給快遞公司。快遞公司將所有的包裹進行打包,然後集中放到運輸車中送到目的地,到了目的地之後然後再進行拆分。
Parcel既包含C++部分的實現,也同時提供了Java的接口,中間通過JNI銜接。Java層的接口其實僅僅是一層包裝,真正的實現都是位於C++部分中,它們的關系如下圖所示:
特別需要說明一下的是,Parcel類除了可以傳遞基本數據類型,還可以傳遞Binder對象:
status_t Parcel::writeStrongBinder(const sp<IBinder>& val) { return flatten_binder(ProcessState::self(), val, this); }
這個方法寫入的是sp<IBinder>
類型的對象,而IBinder既可能是本地Binder,也可能是遠程Binder,這樣我們就不可以不用關心具體細節直接進行Binder對象的傳遞。
這也是為什麼IInterface中定義了兩個asBinder的static方法,如果你不記得了,請回憶一下這兩個方法:
static sp<IBinder> asBinder(const IInterface*); static sp<IBinder> asBinder(const sp<IInterface>&);
而對於Binder驅動,我們前面已經講解過:Binder驅動並不是真的將對象在進程間序列化傳遞,而是由Binder驅動完成了對於Binder對象指針的解釋和翻譯,使調用者看起來就像在進程間傳遞對象一樣。
在講解Binder驅動的時候,我們就講解過驅動中對應線程的管理。這裡我們再來看看,Framework層是如何與驅動層對接進行線程管理的。
ProcessState::setThreadPoolMaxThreadCount
方法中,會通過BINDER_SET_MAX_THREADS
命令設置進程支持的最大線程數量:
#define DEFAULT_MAX_BINDER_THREADS 15 status_t ProcessState::setThreadPoolMaxThreadCount(size_t maxThreads) { status_t result = NO_ERROR; if (ioctl(mDriverFD, BINDER_SET_MAX_THREADS, &maxThreads) != -1) { mMaxThreads = maxThreads; } else { result = -errno; ALOGE("Binder ioctl to set max threads failed: %s", strerror(-result)); } return result; }
由此驅動便知道了該Binder服務支持的最大線程數。驅動在運行過程中,會根據需要,並在沒有超過上限的情況下,通過BR_SPAWN_LOOPER
命令通知進程創建線程:
IPCThreadState
在收到BR_SPAWN_LOOPER
請求之後,便會調用ProcessState::spawnPooledThread
來創建線程:
status_t IPCThreadState::executeCommand(int32_t cmd) { ... case BR_SPAWN_LOOPER: mProcess->spawnPooledThread(false); break; ... }
ProcessState::spawnPooledThread
方法負責為線程設定名稱並創建線程:
void ProcessState::spawnPooledThread(bool isMain) { if (mThreadPoolStarted) { String8 name = makeBinderThreadName(); ALOGV("Spawning new pooled thread, name=%s\n", name.string()); sp<Thread> t = new PoolThread(isMain); t->run(name.string()); } }
線程在run之後,會調用threadLoop
將自身添加的線程池中:
virtual bool threadLoop() { IPCThreadState::self()->joinThreadPool(mIsMain); return false; }
而IPCThreadState::joinThreadPool
方法中,會根據當前線程是否是主線程發送BC_ENTER_LOOPER
或者BC_REGISTER_LOOPER
命令告知驅動線程已經創建完畢。整個調用流程如下圖所示:
單純的理論知識也許並不能讓我們非常好的理解,下面我們以一個具體的Binder服務例子來結合上文的知識進行講解。
下面以PowerManager為例,來看看C++的Binder服務是如何實現的。
下圖是PowerManager C++部分的實現類圖(PowerManager也有Java層的接口,但我們這裡就不討論了)。
圖中Binder Framework中的類我們在上文中已經介紹過了,而PowerManager相關的四個類,便是在Framework的基礎上開發的。
IPowerManager
定義了PowerManager所有對外提供的功能接口,其子類都繼承了這些接口。
BpPowerManager
是提供給客戶端調用的遠程接口BnPowerManager
中只有一個onTransact
方法,該方法根據請求的code來對接每個請求,並直接調用PowerManager
中對應的方法PowerManager
是服務真正的實現在IPowerManager.h中,通過DECLARE_META_INTERFACE(PowerManager);
聲明一些Binder必要的組件。在IPowerManager.cpp中,通過IMPLEMENT_META_INTERFACE(PowerManager, "android.os.IPowerManager");
宏來進行實現。
服務的本地實現主要就是實現BnPowerManager和PowerManager兩個類,PowerManager是BnPowerManager的子類,因此在BnPowerManager中調用自身的virtual
方法其實都是在子類PowerManager類中實現的。
BnPowerManager類要做的就是復寫onTransact
方法,這個方法的職責是:根據請求的code區分具體調用的是那個接口,然後按順序從Parcel中讀出打包好的參數,接著調用留待子類實現的虛函數。需要注意的是:這裡從Parcel讀出參數的順序需要和BpPowerManager
中寫入的順序完全一致,否則讀出的數據將是無效的。
電源服務包含了好幾個接口。雖然每個接口的實現邏輯各不一樣,但從Binder框架的角度來看,它們的實現結構是一樣。而這裡我們並不關心電源服務的實現細節,因此我們取其中一個方法看其實現方式即可。
首先我們來看一下BnPowerManager::onTransact
中的代碼片段:
status_t BnPowerManager::onTransact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { switch (code) { ... case IPowerManager::REBOOT: { CHECK_INTERFACE(IPowerManager, data, reply); bool confirm = data.readInt32(); String16 reason = data.readString16(); bool wait = data.readInt32(); return reboot(confirm, reason, wait); } ... } }
這段代碼中我們看到了實現中是如何根據code區分接口,並通過Parcel讀出調用參數,然後調用具體服務方的。
而PowerManager
這個類才真正是服務實現的本體,reboot方法真正實現了重啟的邏輯:
status_t PowerManager::reboot(bool confirm, const String16& reason, bool wait) { const std::string reason_str(String8(reason).string()); if (!(reason_str.empty() || reason_str == kRebootReasonRecovery)) { LOG(WARNING) << "Ignoring reboot request with invalid reason \"" << reason_str << "\""; return BAD_VALUE; } LOG(INFO) << "Rebooting with reason \"" << reason_str << "\""; if (!property_setter_->SetProperty(ANDROID_RB_PROPERTY, kRebootPrefix + reason_str)) { return UNKNOWN_ERROR; } return OK; }
通過這樣結構的設計,將框架相關的邏輯(BnPowerManager中的實現)和業務本身的邏輯(PowerManager中的實現)徹底分離開了,保證每一個類都非常的“干淨”,這一點是很值得我們在做軟件設計時學習的。
服務實現完成之後,並不是立即就能讓別人使用的。上文中,我們就說到過:所有在Binder上發布的服務必須要注冊到ServiceManager中才能被其他模塊獲取和使用。而在BinderService
類中,提供了publishAndJoinThreadPool
方法來簡化服務的發布,其代碼如下:
static void publishAndJoinThreadPool(bool allowIsolated = false) { publish(allowIsolated); joinThreadPool(); } static status_t publish(bool allowIsolated = false) { sp<IServiceManager> sm(defaultServiceManager()); return sm->addService( String16(SERVICE::getServiceName()), new SERVICE(), allowIsolated); } ... static void joinThreadPool() { sp<ProcessState> ps(ProcessState::self()); ps->startThreadPool(); ps->giveThreadPoolName(); IPCThreadState::self()->joinThreadPool(); }
由此可見,Binder服務的發布其實有三個步驟:
IServiceManager::addService
在ServiceManager中進行服務的注冊ProcessState::startThreadPool
啟動線程池IPCThreadState::joinThreadPool
將主線程加入的Binder中Proxy類是供客戶端使用的。BpPowerManager需要實現IPowerManager中的所有接口。
我們還是以上文提到的reboot接口為例,來看看BpPowerManager::reboot
方法是如何實現的:
virtual status_t reboot(bool confirm, const String16& reason, bool wait) { Parcel data, reply; data.writeInterfaceToken(IPowerManager::getInterfaceDescriptor()); data.writeInt32(confirm); data.writeString16(reason); data.writeInt32(wait); return remote()->transact(REBOOT, data, &reply, 0); }
這段代碼很簡單,邏輯就是:通過Parcel寫入調用參數進行打包,然後調用remote()->transact
將請求發送出去。
其實BpPowerManager中其他方法,甚至所有其他BpXXX中所有的方法,實現都是和這個方法一樣的套路。就是:通過Parcel打包數據,通過remote()->transact
發送數據。而這裡的remote()返回的其實就是BpBinder對象,由此經由IPCThreadState將數據發送到了驅動層。如果你已經不記得,請重新看一下下面這幅圖:
另外,需要一下的是,這裡的REBOOT
就是請求的code,而這個code是在IPowerManager中定義好的,這樣子類可以直接使用,並保證是一致的:
enum { ACQUIRE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION, ACQUIRE_WAKE_LOCK_UID = IBinder::FIRST_CALL_TRANSACTION + 1, RELEASE_WAKE_LOCK = IBinder::FIRST_CALL_TRANSACTION + 2, UPDATE_WAKE_LOCK_UIDS = IBinder::FIRST_CALL_TRANSACTION + 3, POWER_HINT = IBinder::FIRST_CALL_TRANSACTION + 4, UPDATE_WAKE_LOCK_SOURCE = IBinder::FIRST_CALL_TRANSACTION + 5, IS_WAKE_LOCK_LEVEL_SUPPORTED = IBinder::FIRST_CALL_TRANSACTION + 6, USER_ACTIVITY = IBinder::FIRST_CALL_TRANSACTION + 7, WAKE_UP = IBinder::FIRST_CALL_TRANSACTION + 8, GO_TO_SLEEP = IBinder::FIRST_CALL_TRANSACTION + 9, NAP = IBinder::FIRST_CALL_TRANSACTION + 10, IS_INTERACTIVE = IBinder::FIRST_CALL_TRANSACTION + 11, IS_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 12, SET_POWER_SAVE_MODE = IBinder::FIRST_CALL_TRANSACTION + 13, REBOOT = IBinder::FIRST_CALL_TRANSACTION + 14, SHUTDOWN = IBinder::FIRST_CALL_TRANSACTION + 15, CRASH = IBinder::FIRST_CALL_TRANSACTION + 16, };
在服務已經發布之後,客戶端該如何獲取其服務接口然後對其發出請求調用呢?
很顯然,客戶端應該通過BpPowerManager的對象來請求其服務。但看一眼BpPowerManager的構造函數,我們會發現,似乎沒法直接創建一個這類的對象,因為這裡需要一個sp<IBinder>
類型的參數。
BpPowerManager(const sp<IBinder>& impl) : BpInterface<IPowerManager>(impl) { }
那麼這個sp<IBinder>
參數我們該從哪裡獲取呢?
回憶一下前面的內容:Proxy其實是包含了一個指向Server的句柄,所有的請求發送出去的時候都需要包含這個句柄作為一個標識。而想要拿到這個句柄,我們自然應當想到ServiceManager。我們再看一下ServiceManager的接口自然就知道這個sp<IBinder>
該如何獲取了:
/** * Retrieve an existing service, blocking for a few seconds * if it doesn't yet exist. */ virtual sp<IBinder> getService( const String16& name) const = 0; /** * Retrieve an existing service, non-blocking. */ virtual sp<IBinder> checkService( const String16& name) const = 0;
這裡的兩個方法都可以獲取服務對應的sp<IBinder>
對象,一個是阻塞式的,另外一個不是。傳遞的參數是一個字符串,這個就是服務在addServer時對應的字符串,而對於PowerManager來說,這個字符串就是”power”。因此,我們可以通過下面這行代碼創建出BpPowerManager的對象。
sp<IBinder> bs = defaultServiceManager()->checkService(serviceName); sp<IPowerManager> pm = new BpPowerManager(bs);
但這樣做還會存在一個問題:BpPowerManager中的方法調用是經由驅動然後跨進程調用的。通常情況下,當我們的客戶端與PowerManager服務所在的進程不是同一個進程的時候,這樣調用是沒有問題的。那假設我們的客戶端又剛好和PowerManager服務在同一個進程該如何處理呢?
針對這個問題,Binder Framework提供的解決方法是:通過interface_cast
這個方法來獲取服務的接口對象,由這個方法本身根據是否是在同一個進程,來自動確定返回一個本地Binder還是遠程Binder。interface_cast
是一個模板方法,其源碼如下:
template<typename INTERFACE> inline sp<INTERFACE> interface_cast(const sp<IBinder>& obj) { return INTERFACE::asInterface(obj); }
調用這個方法的時候我們需要指定Binder服務的IInterface,因此對於PowerManager,我們需要這樣獲取其Binder接口對象:
const String16 serviceName("power"); sp<IBinder> bs = defaultServiceManager()->checkService(serviceName); if (bs == NULL) { return NAME_NOT_FOUND; } sp<IPowerManager> pm = interface_cast<IPowerManager>(bs);
我們再回頭看一下interface_cast
這個方法體,這裡是在調用INTERFACE::asInterface(obj)
,而對於IPowerManager來說,其實就是IPowerManager::asInterface(obj)
。那麼IPowerManager::asInterface
這個方法是哪裡定義的呢?
這個正是上文提到的DECLARE_META_INTERFACE和IMPLEMENT_META_INTERFACE兩個宏所起的作用。IMPLEMENT_META_INTERFACE宏包含了下面這段代碼:
android::sp<I##INTERFACE> I##INTERFACE::asInterface( \ const android::sp<android::IBinder>& obj) \ { \ android::sp<I##INTERFACE> intr; \ if (obj != NULL) { \ intr = static_cast<I##INTERFACE*>( \ obj->queryLocalInterface( \ I##INTERFACE::descriptor).get()); \ if (intr == NULL) { \ intr = new Bp##INTERFACE(obj); \ } \ } \ return intr; \ } \
這裡我們將“##INTERFACE”通過“PowerManager”代替,得到的結果就是:
android::sp<IPowerManager> IPowerManager::asInterface( const android::sp<android::IBinder>& obj) { android::sp<IPowerManager> intr; if (obj != NULL) { intr = static_cast<IPowerManager*>( obj->queryLocalInterface( IPowerManager::descriptor).get()); if (intr == NULL) { intr = new BpPowerManager(obj); } } return intr; }
這個便是IPowerManager::asInterface
方法的實現,這段邏輯的含義就是:
queryLocalInterface
看看能夠獲得本地Binder,如果是在服務所在進程調用,自然能獲取本地Binder,否則將返回NULL由此保證了:我們在進程內部的調用,是直接通過方法調用的形式。而不在同一個進程的時候,才通過Binder進行跨進程的調用。
前文已經兩次介紹過ServiceManager了,我們知道這個模塊負責了所有Binder服務的管理,並且也看到了Binder驅動中對於這個模塊的實現。可以說ServiceManager是整個Binder IPC的控制中心和交通樞紐。這裡我們就來看一下這個模塊的具體實現。
ServiceManager是一個獨立的可執行文件,在設備中的進程名稱是/system/bin/servicemanager
,這個也是其可執行文件的路徑。
ServiceManager實現源碼的位於這個路徑:frameworks/native/cmds/servicemanager/
,其main函數的主要內容如下:
int main() { struct binder_state *bs; bs = binder_open(128*1024); if (!bs) { ALOGE("failed to open binder driver\n"); return -1; } if (binder_become_context_manager(bs)) { ALOGE("cannot become context manager (%s)\n", strerror(errno)); return -1; } ... binder_loop(bs, svcmgr_handler); return 0; }
這段代碼很簡單,主要做了三件事情:
binder_open(128*1024);
是打開Binder,並指定緩存大小為128k,由於ServiceManager提供的接口很簡單(下文會講到),因此並不需要普通進程那麼多(1M – 8K)的緩存binder_become_context_manager(bs)
使自己成為Context Manager。這裡的Context Manager是Binder驅動裡面的名稱,等同於ServiceManager。binder_become_context_manager
的方法實現只有一行代碼:ioctl(bs->fd, BINDER_SET_CONTEXT_MGR, 0);
看過Binder驅動部分解析的內容,這行代碼應該很容易理解了binder_loop(bs, svcmgr_handler);
是在Looper上循環,等待其他模塊請求服務service_manager.c中的實現與普通Binder服務的實現有些不一樣:並沒有通過繼承接口類來實現,而是通過幾個c語言的函數來完成了實現。這個文件中的主要方法如下:
ServiceManager中,通過svcinfo
結構體來描述已經注冊的Binder服務:
struct svcinfo { struct svcinfo *next; uint32_t handle; struct binder_death death; int allow_isolated; size_t len; uint16_t name[0]; };
next
是一個指針,指向下一個服務,通過這個指針將所有服務串成了鏈表。handle
是指向Binder服務的句柄,這個句柄是由Binder驅動翻譯,指向了Binder服務的實體(參見驅動中:Binder中的“面向對象”),name
是服務的名稱。
ServiceManager的實現邏輯並不復雜,這個模塊就好像在整個系統上提供了一個全局的HashMap而已:通過服務名稱進行服務注冊,然後再通過服務名稱來查找。而真正復雜的邏輯其實都是在Binder驅動中實現了。
源碼路徑:
frameworks/native/include/binder/IServiceManager.h frameworks/native/libs/binder/IServiceManager.cpp
ServiceManager的C++接口定義如下:
class IServiceManager : public IInterface { public: DECLARE_META_INTERFACE(ServiceManager); virtual sp<IBinder> getService( const String16& name) const = 0; virtual sp<IBinder> checkService( const String16& name) const = 0; virtual status_t addService( const String16& name, const sp<IBinder>& service, bool allowIsolated = false) = 0; virtual Vector<String16> listServices() = 0; enum { GET_SERVICE_TRANSACTION = IBinder::FIRST_CALL_TRANSACTION, CHECK_SERVICE_TRANSACTION, ADD_SERVICE_TRANSACTION, LIST_SERVICES_TRANSACTION, }; };
這裡我們看到,ServiceManager提供的接口只有四個,這四個接口說明如下:
這其中,最後一個接口是為了調試而提供的。通過adb shell
連接到設備上之後,可以通過輸入service list
輸出所有注冊的服務列表。這裡”service”可執行文件其實就是通過調用listServices
接口獲取到服務列表的。
service
命令的源碼路徑在這裡:frameworks/native/cmds/service
service list
的輸出看起來像下面這樣(一次輸出可能有一百多個服務,這裡省略了):
255|angler:/ # service list Found 125 services: 0 sip: [android.net.sip.ISipService] 1 nfc: [android.nfc.INfcAdapter] 2 carrier_config: [com.android.internal.telephony.ICarrierConfigLoader] 3 phone: [com.android.internal.telephony.ITelephony] 4 isms: [com.android.internal.telephony.ISms] 5 iphonesubinfo: [com.android.internal.telephony.IPhoneSubInfo] 6 simphonebook: [com.android.internal.telephony.IIccPhoneBook] 7 telecom: [com.android.internal.telecom.ITelecomService] 8 isub: [com.android.internal.telephony.ISub] 9 contexthub_service: [android.hardware.location.IContextHubService] 10 dns_listener: [android.net.metrics.IDnsEventListener] 11 connmetrics: [android.net.IIpConnectivityMetrics] 12 connectivity_metrics_logger: [android.net.IConnectivityMetricsLogger] 13 bluetooth_manager: [android.bluetooth.IBluetoothManager] 14 imms: [com.android.internal.telephony.IMms] 15 media_projection: [android.media.projection.IMediaProjectionManager] 16 launcherapps: [android.content.pm.ILauncherApps] 17 shortcut: [android.content.pm.IShortcutService] 18 fingerprint: [android.hardware.fingerprint.IFingerprintService] 19 trust: [android.app.trust.ITrustManager] 20 media_router: [android.media.IMediaRouterService] ...
普通的Binder服務我們需要通過ServiceManager來獲取接口才能調用,那麼ServiceManager的接口有如何獲得呢?在libbinder中,提供了一個defaultServiceManager
方法來獲取ServiceManager的Proxy,並且這個方法不需要傳入參數。原因我們在驅動篇中也已經講過了:Binder的實現中,為ServiceManager留了一個特殊的位置,不需要像普通服務那樣通過標識去查找。defaultServiceManager
代碼如下:
sp<IServiceManager> defaultServiceManager() { if (gDefaultServiceManager != NULL) return gDefaultServiceManager; { AutoMutex _l(gDefaultServiceManagerLock); while (gDefaultServiceManager == NULL) { gDefaultServiceManager = interface_cast<IServiceManager>( ProcessState::self()->getContextObject(NULL)); if (gDefaultServiceManager == NULL) sleep(1); } } return gDefaultServiceManager; }
本文我們詳細講解了Binder Framework C++層的實現。
但對於Android App開發者來說,絕大部分情況下都是在用Java語言開發。那麼,在下一篇文章中,我就來詳細講解Binder Framework Java層的實現。並且也會講解AIDL與Binder的關系,敬請期待。
我仍記得2014年我決定做安卓開發的那天,這是我一生中做出的最好決定之一。到現在已經有2年半了, 最初的時候,並沒有人告訴我如何做才是正確的。我犯了很多錯誤,浪費
Gallery能夠水平顯示其內容,一般用來浏覽圖片,被選中的選項位於中間,並且可以相應事件顯示信息。下面結合ImageSwitcher組件來實現一個通過縮略圖來浏
Android studio不僅允許你為你的app和依賴庫創建模塊,同時也可為Android wear,Android TV,Google App Engine等
Android安全加密專題文章索引 Android安全加密:對稱加密 Android安全加密:非對稱加密 Android安全加密:消息摘要Message Dig