hwbinder與binder返回值差異之IPCThreadState原理剖析

hi参滴,粉絲朋友們:

背景知識(shí)

針對(duì)hidl中講解到的genarates關(guān)鍵字
https://source.android.google.cn/docs/core/architecture/hidl-cpp/functions

1.png

再稍微總結(jié)一下:
針對(duì)有g(shù)enerates關(guān)鍵字的hidl方法聲明如下:

@callflow(next="*")
createVirtualDisplay(uint32_t width,
                     uint32_t height,
                     PixelFormat formatHint,
                     uint32_t outputBufferSlotCount)
          generates (Error error,
                     Display display,
                     PixelFormat format);

generates就代表返回值部分的回調(diào)末誓,即可以相比return優(yōu)勢(shì)就是可以返回多個(gè)參數(shù),比如這里就可以返回

 generates (Error error,
                     Display display,
                     PixelFormat format);

中的Error error, Display display,PixelFormat format一共3個(gè)參數(shù),正常return的話一般都是一個(gè)參數(shù)些阅,要返回3個(gè)那得自己包裝對(duì)于的對(duì)象檩咱,或者是吧返回值放到參數(shù)中,指針?lè)绞教钊搿?/p>

但也有另外的:
比如看下面這個(gè)情況:

 @callflow(next="*")
    getMaxVirtualDisplayCount() generates (uint32_t count);

如果generates返回參數(shù)只有一個(gè)驯绎,而且這個(gè)參數(shù)類型還是基礎(chǔ)數(shù)據(jù)類型完慧,那么就不會(huì)通過(guò)回調(diào)方式返回,而是直接以返回值方式返回剩失。
這一部分的genarates語(yǔ)法都是hidl屈尼,其實(shí)c++代碼肯定沒(méi)有這些genarates關(guān)鍵字,因?yàn)樗械膆idl文件和aidl文件一樣會(huì)在編譯時(shí)候變成c++

2.png

問(wèn)題提出:

請(qǐng)問(wèn)這個(gè)genarates的實(shí)現(xiàn)原理是怎么樣的拴孤?上面只是知道了genarates可以實(shí)現(xiàn)多個(gè)返回值脾歧,具體怎么實(shí)現(xiàn)的呢?

針對(duì)這個(gè)多個(gè)返回值問(wèn)題演熟,其實(shí)本質(zhì)上還要看一下相關(guān)的源碼:

system/libhwbinder/BpHwBinder.cpp

status_t BpHwBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags, TransactCallback callback)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);

        if (status == ::android::OK && callback != nullptr) {
            callback(*reply);
        }

        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

這里其實(shí)只可以看到這個(gè)callback其實(shí)是在 IPCThreadState::self()->transact調(diào)用后鞭执,對(duì)于的返回值都在 Parcel* reply中司顿,所以調(diào)用callback就只需要傳入*reply既可以。但是這個(gè)地方明顯好像不對(duì)是吧兄纺,和我們上面看到的多個(gè)返回值對(duì)不上大溜。
哈哈,這個(gè)我們就得看傳入的這個(gè)TransactCallback callback到底是怎么樣的估脆,如果傳入的這個(gè)方法里面有進(jìn)行對(duì)parcel進(jìn)行解析钦奋,然后再回調(diào)是不是就可行了

具體拿一個(gè)hwc中的createVirtualDisplay來(lái)看看它的源碼怎么實(shí)現(xiàn)的:


::android::hardware::Return<void> BpHwComposerClient::_hidl_createVirtualDisplay(::android::hardware::IInterface *_hidl_this, ::android::hardware::details::HidlInstrumentor *_hidl_this_instrumentor, uint32_t width, uint32_t height, ::android::hardware::graphics::common::V1_0::PixelFormat formatHint, uint32_t outputBufferSlotCount, createVirtualDisplay_cb _hidl_cb) {
   

  //省略一部分

  //這里開(kāi)始調(diào)用上面的核心BpHwBinder::transact方法,這里傳遞的TransactCallback疙赠,居然是一個(gè)lamada表達(dá)式锨苏,其實(shí)就是處理parcel返回值的一個(gè)回調(diào)方法,所以多個(gè)返回值參數(shù)的秘密都在這個(gè)lamada表達(dá)式的處理中
    _hidl_transact_err = ::android::hardware::IInterface::asBinder(_hidl_this)->transact(3 /* createVirtualDisplay */, _hidl_data, &_hidl_reply, 0 /* flags */, [&] (::android::hardware::Parcel& _hidl_reply) {
        ::android::hardware::graphics::composer::V2_1::Error _hidl_out_error;
        uint64_t _hidl_out_display;
        ::android::hardware::graphics::common::V1_0::PixelFormat _hidl_out_format;


        _hidl_err = ::android::hardware::readFromParcel(&_hidl_status, _hidl_reply);
        if (_hidl_err != ::android::OK) { return; }

        if (!_hidl_status.isOk()) { return; }

        _hidl_err = _hidl_reply.readInt32((int32_t *)&_hidl_out_error);
        if (_hidl_err != ::android::OK) { return; }

        _hidl_err = _hidl_reply.readUint64(&_hidl_out_display);
        if (_hidl_err != ::android::OK) { return; }

        _hidl_err = _hidl_reply.readInt32((int32_t *)&_hidl_out_format);
        if (_hidl_err != ::android::OK) { return; }
//這個(gè)就是核心的回調(diào)多個(gè)返回值
        _hidl_cb(_hidl_out_error, _hidl_out_display, _hidl_out_format);

       
    });
    if (_hidl_transact_err != ::android::OK) {
        _hidl_err = _hidl_transact_err;
        goto _hidl_error;
    }

    if (!_hidl_status.isOk()) { return _hidl_status; }
    return ::android::hardware::Return<void>();

_hidl_error:
    _hidl_status.setFromStatusT(_hidl_err);
    return ::android::hardware::Return<void>(_hidl_status);
}

上面源碼一看是不是就把整個(gè) 多參數(shù)返回值原理給解密了棺聊。

歸納總結(jié)一下:
1伞租、hwbinder的跨進(jìn)程調(diào)用和binder跨進(jìn)程調(diào)用沒(méi)啥區(qū)別,都是調(diào)用了IPCThreadState::self()->transact并沒(méi)有多出啥參數(shù)
2限佩、針對(duì)genarates多參數(shù)返回葵诈,屬于BpHwBinder在接受到了返回值parcel后自己做的一個(gè)包裝解析工作
3、多參數(shù)返回其實(shí)只是自己進(jìn)程跨進(jìn)程后接收到parcel返回后祟同,自己進(jìn)行的一些加工處理返回作喘,并不是說(shuō)多參數(shù)返回值是服務(wù)器端對(duì)客戶端的跨進(jìn)程回調(diào)

3.png

服務(wù)端的多參數(shù)返回執(zhí)行

上面都是Bp端對(duì)多參數(shù)返回的一些處理分析,但在分析Bn源碼時(shí)候晕城,也發(fā)現(xiàn)Bn端也有這個(gè)多參數(shù)返回值相關(guān)的回調(diào)參數(shù)傳遞泞坦,上面不是說(shuō)了不是遠(yuǎn)端的回調(diào)嗎?怎么Bn端還需要有這個(gè)砖顷,這里也來(lái)解密一下:

system/libhwbinder/IPCThreadState.cpp


status_t IPCThreadState::executeCommand(int32_t cmd)
{
   

    case BR_TRANSACTION_SEC_CTX:
    case BR_TRANSACTION:
        {


            auto reply_callback = [&] (auto &replyParcel) {
                if (reply_sent) {
                    // Reply was sent earlier, ignore it.
                    ALOGE("Dropping binder reply, it was sent already.");
                    return;
                }
                reply_sent = true;
                if ((tr.flags & TF_ONE_WAY) == 0) {
                    replyParcel.setError(NO_ERROR);
                    sendReply(replyParcel, (tr.flags & kForwardReplyFlags));
                } else {
                    ALOGE("Not sending reply in one-way transaction");
                }
            };

            if (tr.target.ptr) {
        
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                       //這里進(jìn)行BHwBinder::transact調(diào)用贰锁,注意這里面?zhèn)鬟f了reply_callback,reply_callback就上面定義的lamada表達(dá)式
                    error = reinterpret_cast<BHwBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags, reply_callback);
                    reinterpret_cast<BHwBinder*>(tr.cookie)->decStrong(this);
                } else {
                    error = UNKNOWN_TRANSACTION;
                }

            } else {
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags, reply_callback);
            }

            if ((tr.flags & TF_ONE_WAY) == 0) {
                if (!reply_sent) {//一般變成了true滤蝠,因?yàn)樯厦嬗谢卣{(diào)情況
                    // Should have been a reply but there wasn't, so there
                    // must have been an error instead.
                    reply.setError(error);
                    sendReply(reply, (tr.flags & kForwardReplyFlags));
                } else {
                    if (error != NO_ERROR) {
                        ALOGE("transact() returned error after sending reply.");
                    } else {
                        // Ok, reply sent and transact didn't return an error.
                    }
                }
            } else {
                // One-way transaction, don't care about return value or reply.
            }
        }
        break;


    return result;
}

system/libhwbinder/Binder.cpp

status_t BHwBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags, TransactCallback callback)
{
    data.setDataPosition(0);

//這里的核心是調(diào)用onTransact方法豌熄,注意這里也有一個(gè)lamada表達(dá)式傳遞進(jìn)去了
    return onTransact(code, data, reply, flags, [&](auto& replyParcel) {
      replyParcel.setDataPosition(0);
      if (callback != nullptr) {
      //這里callback其實(shí)就是執(zhí)行一下上面的IPCThreadState的reply_callback,其實(shí)reply_callback里面沒(méi)有干啥核心事情物咳,就是調(diào)用了sendReply
        callback(replyParcel);
      }
    });
}

來(lái)看看hidl對(duì)于的Bn代碼

::android::status_t BnHwComposerClient::onTransact(
        uint32_t _hidl_code,
        const ::android::hardware::Parcel &_hidl_data,
        ::android::hardware::Parcel *_hidl_reply,
        uint32_t _hidl_flags,
        TransactCallback _hidl_cb) {
    ::android::status_t _hidl_err = ::android::OK;

    switch (_hidl_code) {
        
        case 3 /* createVirtualDisplay */:
        {
       //調(diào)用到了_hidl_createVirtualDisplay有這個(gè)_hidl_cb
            _hidl_err = ::android::hardware::graphics::composer::V2_1::BnHwComposerClient::_hidl_createVirtualDisplay(this, _hidl_data, _hidl_reply, _hidl_cb);
            break;
        }
}
//_hidl_createVirtualDisplay實(shí)現(xiàn)
::android::status_t BnHwComposerClient::_hidl_createVirtualDisplay(
        ::android::hidl::base::V1_0::BnHwBase* _hidl_this,
        const ::android::hardware::Parcel &_hidl_data,
        ::android::hardware::Parcel *_hidl_reply,
        TransactCallback _hidl_cb) {
   
    bool _hidl_callbackCalled = false;

//注意這里調(diào)用實(shí)現(xiàn)時(shí)候最后一個(gè)參數(shù)就是一個(gè)多個(gè)返回值參數(shù)的lamada表達(dá)是锣险,讓實(shí)現(xiàn)里面吧多個(gè)返回值回調(diào)這個(gè)lamada
    ::android::hardware::Return<void> _hidl_ret = static_cast<IComposerClient*>(_hidl_this->getImpl().get())->createVirtualDisplay(width, height, formatHint, outputBufferSlotCount, [&](const auto &_hidl_out_error, const auto &_hidl_out_display, const auto &_hidl_out_format) {
      
        _hidl_callbackCalled = true;

        ::android::hardware::writeToParcel(::android::hardware::Status::ok(), _hidl_reply);
                //這里會(huì)_hidl_reply進(jìn)行寫(xiě)入對(duì)于的多個(gè)返回值參數(shù)
        _hidl_err = _hidl_reply->writeInt32((int32_t)_hidl_out_error);
        if (_hidl_err != ::android::OK) { goto _hidl_error; }

        _hidl_err = _hidl_reply->writeUint64(_hidl_out_display);
        if (_hidl_err != ::android::OK) { goto _hidl_error; }

        _hidl_err = _hidl_reply->writeInt32((int32_t)_hidl_out_format);
        if (_hidl_err != ::android::OK) { goto _hidl_error; }

  

        if (_hidl_err != ::android::OK) { return; }
       //最后裝好多個(gè)返回值數(shù)據(jù)的parcel進(jìn)行回調(diào)并且?guī)蟨arcel
        _hidl_cb(*_hidl_reply);
    });

    _hidl_ret.assertOk();
    if (!_hidl_callbackCalled) {
        LOG_ALWAYS_FATAL("createVirtualDisplay: _hidl_cb not called, but must be called once.");
    }

    return _hidl_err;
}

下面再看看具體實(shí)現(xiàn)static_cast<IComposerClient*>(_hidl_this->getImpl().get())里面的實(shí)現(xiàn):
hardware/interfaces/graphics/composer/2.1/utils/hal/include/composer-hal/2.1/ComposerClient.h

Return<void> createVirtualDisplay(uint32_t width, uint32_t height, PixelFormat formatHint,
                                      uint32_t outputBufferSlotCount,
                                      IComposerClient::createVirtualDisplay_cb hidl_cb) override {
        Display display = 0;
        Error err = mHal->createVirtualDisplay(width, height, &formatHint, &display);
        if (err == Error::NONE) {
            mResources->addVirtualDisplay(display, outputBufferSlotCount);
        }
            //這里是最開(kāi)始的多參數(shù)回調(diào)
        hidl_cb(err, display, formatHint);
        return Void();
    }

IComposerClient::createVirtualDisplay_cb 其實(shí)也是工具生成的,路徑如下:
/android.hardware.graphics.composer@2.1_genc++_headers/gen/android/hardware/graphics/composer/2.1/IComposerClient.h

using createVirtualDisplay_cb = std::function<void(::android::hardware::graphics::composer::V2_1::Error error, uint64_t display, ::android::hardware::graphics::common::V1_0::PixelFormat format)>

好了上面基本上就清楚了整個(gè)hidl多參數(shù)返回的原理览闰,從bp端到bn端都進(jìn)行了詳細(xì)原理剖析芯肤。

更多framework干貨課程如下(需要的可以私聊馬哥 獲取優(yōu)惠 +V :androidframework007):


6.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市压鉴,隨后出現(xiàn)的幾起案子崖咨,更是在濱河造成了極大的恐慌,老刑警劉巖晴弃,帶你破解...
    沈念sama閱讀 219,188評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掩幢,死亡現(xiàn)場(chǎng)離奇詭異逊拍,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)际邻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)芯丧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人世曾,你說(shuō)我怎么就攤上這事缨恒。” “怎么了轮听?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,562評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵骗露,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我血巍,道長(zhǎng)萧锉,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,893評(píng)論 1 295
  • 正文 為了忘掉前任述寡,我火速辦了婚禮柿隙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鲫凶。我一直安慰自己禀崖,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布螟炫。 她就那樣靜靜地躺著波附,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昼钻。 梳的紋絲不亂的頭發(fā)上掸屡,一...
    開(kāi)封第一講書(shū)人閱讀 51,708評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音换吧,去河邊找鬼折晦。 笑死钥星,一個(gè)胖子當(dāng)著我的面吹牛沾瓦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播谦炒,決...
    沈念sama閱讀 40,430評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼贯莺,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了宁改?” 一聲冷哼從身側(cè)響起缕探,我...
    開(kāi)封第一講書(shū)人閱讀 39,342評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎还蹲,沒(méi)想到半個(gè)月后爹耗,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體耙考,經(jīng)...
    沈念sama閱讀 45,801評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評(píng)論 3 337
  • 正文 我和宋清朗相戀三年潭兽,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了倦始。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,115評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡山卦,死狀恐怖鞋邑,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情账蓉,我是刑警寧澤枚碗,帶...
    沈念sama閱讀 35,804評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站铸本,受9級(jí)特大地震影響肮雨,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜箱玷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評(píng)論 3 331
  • 文/蒙蒙 一酷含、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧汪茧,春花似錦椅亚、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,008評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至扩灯,卻和暖如春媚赖,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背珠插。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,135評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工惧磺, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人捻撑。 一個(gè)月前我還...
    沈念sama閱讀 48,365評(píng)論 3 373
  • 正文 我出身青樓磨隘,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親顾患。 傳聞我的和親對(duì)象是個(gè)殘疾皇子番捂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容