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
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 其实也是工具生成的,路径如下:
/[email protected]_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):