Android IPC機制4-ServiceManager的addService與getService實現

普通client或者server在獲得servicemanger的proxy對象后,肯定就要使用了。對于server來說,主要是調用addService,向serivceManager注冊。而client則是通過serivcemanager查詢所需server的信息,然后得到server的proxy對象。

注冊服務-addService

以Native層的服務mediaservice為例,我們先來分析下server是如何向SerivceManager注冊的吧

先來看入口 main_mediaserver.cpp的main函數中的與ServiceManager相關的代碼:

int main(int argc __unused, char** argv)
{
    ...

    //獲得ProcessState實例對象
    sp<ProcessState> proc(ProcessState::self());     
    //獲取ServiceManager實例對象 【既BpServiceManager】
    sp<IServiceManager> sm = defaultServiceManager(); 
    AudioFlinger::instantiate(); 
    //多媒體服務            
    MediaPlayerService::instantiate();               
    ResourceManagerService::instantiate(); 
    CameraService::instantiate();         
    AudioPolicyService::instantiate();  
    SoundTriggerHwService::instantiate(); 
    RadioService::instantiate(); 
    registerExtensions();
    //創建Binder線程,并加入線程池
    ProcessState::self()->startThreadPool();  
    //當前線程加入到線程池    
    IPCThreadState::self()->joinThreadPool();    
 }

proc(ProcessState::self())

首先調用的函數是ProcessState::self(),獲得ProcessState對象,ProcessState位置在framework\base\libs\binder\ProcessState.cpp,在上一篇文章里有過講述。其內部有Binder驅動的一些配置,每個進程只有一個。

獲取到ProcessState對象后賦值給了proc變量,程序運行完,proc會自動delete內部的內容,所以就自動釋放了先前分配的資源。

MediaPlayerService:instantiate

void MediaPlayerService::instantiate() {
    defaultServiceManager()->addService(
           String16("media.player"), new MediaPlayerService()); 
}

MediaPlayerService的初始化其實就是調用defaultServiceManager的addserivce方法向servicemanager注冊,而上一篇已經講過了defaultServiceManager,此處不在贅述。defaultServiceManager最終得到的是BpServiceManager。
下面來看看BpServiceManager的addService方法:

virtual status_t addService(const String16& name, const sp<IBinder>& service,
        bool allowIsolated)
{
    Parcel data, reply; //Parcel是數據通信包
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());   //先把Interface名字寫進去,也就是什么android.os.IServiceManager
    data.writeString16(name);        // name為 "media.player"
    data.writeStrongBinder(service); // MediaPlayerService對象
    data.writeInt32(allowIsolated ? 1 : 0); // allowIsolated= false
    status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); //
    return err == NO_ERROR ? reply.readExceptionCode() : 
}

上面函數的核心就是status_t err = remote()->transact(ADD_SERVICE_TRANSACTION, data, &reply); ,而remote()就是創建bpservicemanager時的bpbinder(handle=0)

BpBinder::transact()

來看看BpBinder的Transact函數:

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;
}

可以發現,transact函數還不是在這個執行的,而是調用的IPCThreadState的transact.

IPCThreadState::self

看到這個形式就知道肯定是單例模式了,確實也是這樣

IPCThreadState* IPCThreadState::self()
{
    if (gHaveTLS) { 
restart:
        const pthread_key_t k = gTLS;
        IPCThreadState* st = (IPCThreadState*)pthread_getspecific(k);
        if (st) return st;
        return new IPCThreadState;  //初始IPCThreadState 
    }
    
    if (gShutdown) return NULL;
    
    pthread_mutex_lock(&gTLSMutex);
    if (!gHaveTLS) { //首次進入gHaveTLS為false
        if (pthread_key_create(&gTLS, threadDestructor) != 0) { //創建線程的TLS
            pthread_mutex_unlock(&gTLSMutex);
            return NULL;
        }
        gHaveTLS = true;
    }
    pthread_mutex_unlock(&gTLSMutex);
    goto restart;
}

IPCThreadState::IPCThreadState()
    : mProcess(ProcessState::self()),
      mMyThreadId(gettid()),    
      mStrictModePolicy(0),
      mLastTransactionBinderFlags(0)
{
    pthread_setspecific(gTLS, this);
    clearCaller();
    mIn.setDataCapacity(256);
    mOut.setDataCapacity(256);
}

TLS是指Thread local storage(線程本地儲存空間),每個線程都擁有自己的TLS,并且是私有空間,線程之間不會共享,,和java中的ThreadLocal是差不多的概念。通過pthread_getspecific/pthread_setspecific函數可以獲取/設置這些空間中的內容。從線程本地存儲空間中獲得保存在其中的IPCThreadState對象。

IPCThreadState的構造函數中則是對所屬進程,現成id,優先級等做了一些設置。需要注意的是mInmOut,后面會用到。

IPCThreadState::transact

回到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) {
           //調用writeTransactionData 發送數據
        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) { //flgs=0進入該分支
        if (reply) {
            //等待響應  
            err = waitForResponse(reply);
        } else {
            Parcel fakeReply;
            err = waitForResponse(&fakeReply);
        }

    } else {
        //不需要響應消息的binder則進入該分支
        err = waitForResponse(NULL, NULL); 
    }
    return err;
}

上面代碼的核心就是writeTransactionData 發送數據,waitForResponse等待響應,來看看這兩個函數
writeTransactionData:

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;
    tr.target.handle = handle; // handle=0
    tr.code = code;            // ADD_SERVICE_TRANSACTION
    tr.flags = binderFlags;    // 0 
    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);
    }
//上面把命令數據封裝成binder_transaction_data,然后
//寫到mOut中,mOut是命令的緩沖區,也是一個Parcel
    
    mOut.writeInt32(cmd);         //cmd = BC_TRANSACTION
    mOut.write(&tr, sizeof(tr));  //寫入binder_transaction_data數據
    
    return NO_ERROR;
}

其中handle的值用來標識目的端,注冊服務過程的目的端為service manager,此處handle=0所對應的是binder_context_mgr_node對象,正是service manager所對應的binder實體對象。binder_transaction_data結構體是binder驅動通信的數據結構,該過程最終是把Binder請求碼BC_TRANSACTION和binder_transaction_data結構體寫入到mOut。

再來看看waitForResponse函數

status_t IPCThreadState::waitForResponse(Parcel *reply, status_t *acquireResult)
{
    int32_t cmd;
    int32_t err;

    while (1) {
        if ((err=talkWithDriver()) < NO_ERROR) break; // 
        err = mIn.errorCheck();
        if (err < NO_ERROR) break;
        if (mIn.dataAvail() == 0) continue;
  //這里開始操作mIn了,看來talkWithDriver中
//把mOut發出去,然后從driver中讀到數據放到mIn中了。
        cmd = mIn.readInt32();

        switch (cmd) {
        case BR_TRANSACTION_COMPLETE:
            if (!reply && !acquireResult) goto finish;
            break;
        
        case BR_DEAD_REPLY:
            err = DEAD_OBJECT;
            goto finish;

        case BR_FAILED_REPLY:
            err = FAILED_TRANSACTION;
            goto finish;
        
        case BR_ACQUIRE_RESULT:
            {
                const int32_t result = mIn.readInt32();
                if (!acquireResult) continue;
                *acquireResult = result ? NO_ERROR : INVALID_OPERATION;
            }
            goto finish;
        
        case BR_REPLY:
            {
                binder_transaction_data tr;
                err = mIn.read(&tr, sizeof(tr));
                if (err != NO_ERROR) goto finish;

                if (reply) {
                    if ((tr.flags & TF_STATUS_CODE) == 0) {
                        reply->ipcSetDataReference(
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t),
                            freeBuffer, this);
                    } else {
                        err = *reinterpret_cast<const status_t*>(tr.data.ptr.buffer);
                        freeBuffer(NULL,
                            reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                            tr.data_size,
                            reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                            tr.offsets_size/sizeof(binder_size_t), this);
                    }
                } else {
                    freeBuffer(NULL,
                        reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                        tr.data_size,
                        reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                        tr.offsets_size/sizeof(binder_size_t), this);
                    continue;
                }
            }
            goto finish;

        default:
            err = executeCommand(cmd);  //
            if (err != NO_ERROR) goto finish;
            break;
        }
    }

finish:
    if (err != NO_ERROR) {
        if (acquireResult) *acquireResult = err;
        if (reply) reply->setError(err);
        mLastError = err;
    }
    
   

上面代碼的細節就不深究了,到這里,我們發送addService的流程就徹底走完了。
其實就是BpServiceManager發送了一個addService命令到BnServiceManager,然后收到回復。

回到開始的mediaservice的main函數,在將MediaPlayerService、CameraService、SoundTriggerHwService等服務都初始化后,就會走到下面的語句:

ProcessState::self()->startThreadPool();  
    //當前線程加入到線程池    
IPCThreadState::self()->joinThreadPool(); 

進入兩個函數內部看看

...

看看startThreadPool吧

void ProcessState::startThreadPool()

{

  ...

    spawnPooledThread(true);

}

void ProcessState::spawnPooledThread(bool isMain)

{

    sp<Thread> t = new PoolThread(isMain);isMain是TRUE

//創建線程池,然后run起來,和java的Threadf非常像

    t->run(buf);

 }

//PoolThread從Thread類中派生,那么此時會產生一個線程嗎?看看PoolThread和Thread的構造吧

PoolThread::PoolThread(bool isMain)

        : mIsMain(isMain)

    {

    }
//再來看看父類
Thread::Thread(bool canCallJava)//canCallJava默認值是true

    :   mCanCallJava(canCallJava),

        mThread(thread_id_t(-1)),

        mLock("Thread::mLock"),

        mStatus(NO_ERROR),

        mExitPending(false), mRunning(false)

{

}

//喔,這個時候還沒有創建線程呢。然后調用PoolThread::run,實際調用了基類的run。

status_t Thread::run(const char* name, int32_t priority, size_t stack)

{

  bool res;

    if (mCanCallJava) {

        res = createThreadEtc(_threadLoop,//線程函數是_threadLoop

                this, name, priority, stack, &mThread);

    }

//終于,在run函數中,創建線程了。從此

//主線程執行IPCThreadState::self()->joinThreadPool();新開的線程執行_threadLoop

//我們先看看_threadLoop

int Thread::_threadLoop(void* user)

{

    Thread* const self = static_cast<Thread*>(user);

    sp<Thread> strong(self->mHoldSelf);

    wp<Thread> weak(strong);

    self->mHoldSelf.clear();

 

    do {

 ...

        if (result && !self->mExitPending) {

                result = self->threadLoop();哇塞,調用自己的threadLoop

            }

        }

//我們是PoolThread對象,所以調用PoolThread的threadLoop函數

virtual bool PoolThread ::threadLoop()

    {

//mIsMain為true。

//而且注意,這是一個新的線程,所以必然會創建一個

//新的IPCThreadState對象(記得線程本地存儲嗎?TLS),然后      

IPCThreadState::self()->joinThreadPool(mIsMain);

        return false;

    }

//主線程和工作線程都調用了joinThreadPool,看看這個干嘛了!

void IPCThreadState::joinThreadPool(bool isMain)

{

     mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

     status_t result;

    do {

        int32_t cmd;

         result = talkWithDriver();

         result = executeCommand(cmd);

        }

       } while (result != -ECONNREFUSED && result != -EBADF);

 

    mOut.writeInt32(BC_EXIT_LOOPER);

    talkWithDriver(false);

}

//看到沒?有loop了,但是好像是有兩個線程都執行了這個啊!這里有兩個消息循環?

//下面看看executeCommand

status_t IPCThreadState::executeCommand(int32_t cmd)

{

BBinder* obj;

    RefBase::weakref_type* refs;

    status_t result = NO_ERROR;

case BR_TRANSACTION:

        {

            binder_transaction_data tr;

            result = mIn.read(&tr, sizeof(tr));

//來了一個命令,解析成BR_TRANSACTION,然后讀取后續的信息

       Parcel reply;

             if (tr.target.ptr) {

//這里用的是BBinder。(因為自己是做server端)

                sp<BBinder> b((BBinder*)tr.cookie);

                const status_t error = b->transact(tr.code, buffer, &reply, 0);

}

//讓我們看看BBinder的transact函數干嘛了

status_t BBinder::transact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

//就是調用自己的onTransact函數嘛      

err = onTransact(code, data, reply, flags);

    return err;

}

BnMediaPlayerService從BBinder派生,所以會調用到它的onTransact函數
終于水落石出了,讓我們看看BnMediaPlayerServcice的onTransact函數。

status_t BnMediaPlayerService::onTransact(

    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)

{

// BnMediaPlayerService從BBinder和IMediaPlayerService派生,所有IMediaPlayerService

//看到下面的switch沒?所有IMediaPlayerService提供的函數都通過命令類型來區分

//

    switch(code) {

        case CREATE_URL: {

            CHECK_INTERFACE(IMediaPlayerService, data, reply);

            create是一個虛函數,由MediaPlayerService來實現!!

sp<IMediaPlayer> player = create(

                    pid, client, url, numHeaders > 0 ? &headers : NULL);

 

            reply->writeStrongBinder(player->asBinder());

            return NO_ERROR;

        } break;

其實,到這里,我們就明白了。BnXXX的onTransact函數收取命令,然后派發到派生類的函數,由他們完成實際的工作。
說明:
這里有點特殊,startThreadPool和joinThreadPool完后確實有兩個線程,主線程和工作線程,而且都在做消息循環。

小節

過程分析:

  1. MediaPlayerService進程獲得bpservicemanager,通過bpservicemanager對象的bpbinder成員(handl=0)中轉到IPCThreadState執行transact

  2. IPCThreadState調用writeTransactionData函數向mout緩沖區寫入數據,然后調用waitForResponse等待響應。

  3. mediaservice開啟一個工作線程,和主線程一起開始做消息循環,不斷的與binder驅動通信,故后續更加需求Binder驅動會增加binder線程個數。

獲取服務-getService

再來看看client如何通過servicemanager獲得server的bpbinder的。

getService

首先來看看IServiceManager的getService函數:

virtual sp<IBinder> getService(const String16& name) const
    {
        unsigned n;
        for (n = 0; n < 5; n++){
            sp<IBinder> svc = checkService(name);
            if (svc != NULL) return svc;
            sleep(1);
        }
        return NULL;
    }

核心是checkService(name),進入代碼看看

virtual sp<IBinder> checkService( const String16& name) const
{
    Parcel data, reply;
    //寫入RPC頭
    data.writeInterfaceToken(IServiceManager::getInterfaceDescriptor());
    //寫入服務名
    data.writeString16(name); 
    remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply); 
    return reply.readStrongBinder();
}

remote()->transact(CHECK_SERVICE_TRANSACTION, data, &reply);,這句和之前addService的非常類似,肯定又是通過bpservicemanager最終流轉到IPCThreadState的transact,只是傳遞的參數有所不同罷了。 這里就不再講了。

總結

本片文章說是講ServiceManager的addServicegetService,其實主要還是在分析transact這個函數的執行流轉過程,上面有些代碼細節我也沒有完全弄懂,但我覺得也夠了。簡單的說ServiceMnager通信的核心有以下幾個:

  1. IPCProcessState和IPCThreadState這兩個類,他們分別記錄了進程和線程中IPC通信需要的相關要素。這兩個類都是單例模式,IPCThreadState是最終通信的執行者。
  2. BpBinder:通過handl=0可以直接與ServiceManager的實體Binder通信
  3. Server端會在主線程和一個工作線程開始消息循環,來處理和binder驅動的交互,如有需求再開啟新的線程。
  4. 函數調用的流轉過程:通過一次次的流轉,每個部分都做了需要自己做的事,然后由一個最終的執行者去執行。

文章里很多部分都來自與gityuaninnost,在此表示感謝!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容