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

hi,粉丝朋友们:

背景知识

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

在这里插入图片描述
再稍微总结一下:
针对有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++

在这里插入图片描述

问题提出:

请问这个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返回后,自己进行的一些加工处理返回,并不是说多参数返回值是服务器端对客户端的跨进程回调

在这里插入图片描述

服务端的多参数返回执行

上面都是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):
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值