hwbinder与binder返回值差异之IPCThreadState原理剖析

hi,粉丝朋友们:

背景知识

针对hidl中讲解到的genarates关键字
https://source.android.google.cn/docs/core/architecture/hidl-cpp/functions

1.png

再稍微总结一下:
针对有generates关键字的hidl方法声明如下:

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

generates就代表返回值部分的回调,即可以相比return优势就是可以返回多个参数,比如这里就可以返回

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

中的Error error, Display display,PixelFormat format一共3个参数,正常return的话一般都是一个参数,要返回3个那得自己包装对于的对象,或者是吧返回值放到参数中,指针方式填入。

但也有另外的:
比如看下面这个情况:

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

如果generates返回参数只有一个,而且这个参数类型还是基础数据类型,那么就不会通过回调方式返回,而是直接以返回值方式返回。
这一部分的genarates语法都是hidl,其实c++代码肯定没有这些genarates关键字,因为所有的hidl文件和aidl文件一样会在编译时候变成c++

2.png

问题提出:

请问这个genarates的实现原理是怎么样的?上面只是知道了genarates可以实现多个返回值,具体怎么实现的呢?

针对这个多个返回值问题,其实本质上还要看一下相关的源码:

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

这里其实只可以看到这个callback其实是在 IPCThreadState::self()->transact调用后,对于的返回值都在 Parcel* reply中,所以调用callback就只需要传入*reply既可以。但是这个地方明显好像不对是吧,和我们上面看到的多个返回值对不上。
哈哈,这个我们就得看传入的这个TransactCallback callback到底是怎么样的,如果传入的这个方法里面有进行对parcel进行解析,然后再回调是不是就可行了

具体拿一个hwc中的createVirtualDisplay来看看它的源码怎么实现的:


::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) {
   

  //省略一部分

  //这里开始调用上面的核心BpHwBinder::transact方法,这里传递的TransactCallback,居然是一个lamada表达式,其实就是处理parcel返回值的一个回调方法,所以多个返回值参数的秘密都在这个lamada表达式的处理中
    _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; }
//这个就是核心的回调多个返回值
        _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);
}

上面源码一看是不是就把整个 多参数返回值原理给解密了。

归纳总结一下:
1、hwbinder的跨进程调用和binder跨进程调用没啥区别,都是调用了IPCThreadState::self()->transact并没有多出啥参数
2、针对genarates多参数返回,属于BpHwBinder在接受到了返回值parcel后自己做的一个包装解析工作
3、多参数返回其实只是自己进程跨进程后接收到parcel返回后,自己进行的一些加工处理返回,并不是说多参数返回值是服务器端对客户端的跨进程回调

3.png

服务端的多参数返回执行

上面都是Bp端对多参数返回的一些处理分析,但在分析Bn源码时候,也发现Bn端也有这个多参数返回值相关的回调参数传递,上面不是说了不是远端的回调吗?怎么Bn端还需要有这个,这里也来解密一下:

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)) {
                       //这里进行BHwBinder::transact调用,注意这里面传递了reply_callback,reply_callback就上面定义的lamada表达式
                    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,因为上面有回调情况
                    // 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);

//这里的核心是调用onTransact方法,注意这里也有一个lamada表达式传递进去了
    return onTransact(code, data, reply, flags, [&](auto& replyParcel) {
      replyParcel.setDataPosition(0);
      if (callback != nullptr) {
      //这里callback其实就是执行一下上面的IPCThreadState的reply_callback,其实reply_callback里面没有干啥核心事情,就是调用了sendReply
        callback(replyParcel);
      }
    });
}

来看看hidl对于的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 */:
        {
       //调用到了_hidl_createVirtualDisplay有这个_hidl_cb
            _hidl_err = ::android::hardware::graphics::composer::V2_1::BnHwComposerClient::_hidl_createVirtualDisplay(this, _hidl_data, _hidl_reply, _hidl_cb);
            break;
        }
}
//_hidl_createVirtualDisplay实现
::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;

//注意这里调用实现时候最后一个参数就是一个多个返回值参数的lamada表达是,让实现里面吧多个返回值回调这个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);
                //这里会_hidl_reply进行写入对于的多个返回值参数
        _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; }
       //最后装好多个返回值数据的parcel进行回调并且带上parcel
        _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;
}

下面再看看具体实现static_cast<IComposerClient*>(_hidl_this->getImpl().get())里面的实现:
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);
        }
            //这里是最开始的多参数回调
        hidl_cb(err, display, formatHint);
        return Void();
    }

IComposerClient::createVirtualDisplay_cb 其实也是工具生成的,路径如下:
/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)>

好了上面基本上就清楚了整个hidl多参数返回的原理,从bp端到bn端都进行了详细原理剖析。

更多framework干货课程如下(需要的可以私聊马哥 获取优惠 +V :androidframework007):


6.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,524评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,869评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,813评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,210评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,085评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,117评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,533评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,219评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,487评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,582评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,362评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,218评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,589评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,899评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,176评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,503评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,707评论 2 335

推荐阅读更多精彩内容