Android O Treble框架笔记(基于高通845平台)
tags: android
0 前言
1 关于Treble架构
这里关于treble的什么结构图呀之类的我就不贴了,反正讲treble的文章基本上都贴了一毛一样的图,这里就推荐几个我觉得讲的比较好的文章链接吧,看后面相关内容时默认已经掌握了以下链接中所述的内容。
AndroidO Treble架构分析
AndroidO Treble架构下的变化
AndroidO Treble架构下的接口文件编译
AndroidO Treble架构下Hal进程启动及HIDL服务注册过程
AndroidO Treble架构下Binder对象的转换过程
AndroidO Treble架构下hwservicemanager启动过程
上面6篇文章里面前面3篇比较简单,随便看看,关键的是AndroidO Treble架构下Hal进程启动及HIDL服务注册过程和AndroidO Treble架构下Binder对象的转换过程这两篇,如果对binder完全没什么概念的话可能比较难,但是只要自己结合代码实际跟以下就明白了。最后一篇了解以下没什么坏处。
2 hardware中的IBinder与IBase
2.1 关于binder
一般来说,Binder通信中由一个Bnxxx和一个Bpxxx组成,Bnxxx是指一个本地的Binder对象,或者理解为一个主机,Bpxxx是指一个远程Binder对象或者理解为从机。这里的远程我的理解是指的另外一个线程中的Binder对象,也就是需要通信的对端线程,对端线程有一个Bnxxx的对象,然后本地需要一个Bpxxx来与Bnxxx进行交互。换句话说就是如果我要与另外一个线程,那么对面的线程必须有一个主机(Bnxxx),然后我这里需要有一个从机(Bpxxx)才能进行通信。
在Hardware中,binder文件位于:system\libhwbinder\Binder.cpp
IBinder:hwbinder体系的所有类的父类,其中目前已知方法:
- localBinder:如果此binder在通信中处于主机地位,则返回有效值,否则返回NULL,在binder类中该函数返回NULL,需要子类根据实际情况继承与实现;
- remoteBinder:与localBInder完全相反,这两者一般有且仅有一个返回非NULL而另外一个返回NULL;
BHwBinder:继承自IBinder,在通信中处于主机地位的模块需要继承自该类,其中已知方法:
- transact:发送需要发送的消息,一般需要重构该函数;
- onTransact:处理消息,一般需要重构该函数,以实现特定消息相应;
- localBinder:重载Binder中的该函数,使其返回this;
BpHwRefBase:hwbinder体系中与binder同级的基类,被BpInterface类继承,详见"system\libhwbinder\include\IInterface.h",其中已知方法:
- mRemote成员:一般为BpHWxxx对象(该对象必须继承自IBinder接口),用来跟binder通信中的主机通信的接口,一般来说,最终都是通过IPCThreadState完成最终的数据传输操作;
- remote方法:返回mRemote成员,可以通过该成员实现BpHWBinder相关操作;
在Hardware中与BHwBinder
对等的类是BHwBinder
类,该类位于:system\libhwbinder\BpHwBinder.cpp
BpHwBinder:继承自IBinder,在通信中处于从机地位的模块需要继承自该类,与BHwBinder地位相同,其中已知方法:
- transact:最终通过IPCThreadState::self()->transact发送消息
- remoteBinder:重载Binder中的该函数,返回this,同时没有重载localBinder,所以localBinder返回NULL;
2.2 关于base
IBase对象是Binder通信中的业务逻辑对象。
举个栗子:
binder通信中所谓的远程调用……既然要能调用函数,那么一定有一个对象,这个对象提供了一些方法。于是binder就这样实现了:
1、定义一个接口,继承自IBase
2、找一个类实现这个接口
3、在本地实例化这个类
4、本地程序调用这个类的接口
这样就完成了远程调用。
对于没有看过这块代码的人可能有些懵逼……感觉上面的做法就是一个普通的本地函数调用,跟远程调用没有半毛钱关系……其实本来就是这样,他确确实实就是个本地调用,这个世界上本来就不存在正真意义上的远程调用这种魔术……binder说白了就是一个基于消息的通信机制……关于google宣称的binder机制有多么多么的好,性能多么多么的强,其实没有那么夸张,其他手段和方法也可以实现比binder效率更高的通信!但我这里并不是要否定binder,我觉得binder更主要的贡献实在安全方面,binder的存在是权衡了安全和效率之后的产物,所以是非常有意义的,但是并不是神话。
这里有个比较关键的地方,就是找一个类实现这个接口,这个类是通过工具自动生成的,在AndroidO Treble架构下的接口文件编译里面已经说的很清楚了,至于自动生成的类里面方法的具体实现后面会分析,这里暂时跳过。
所以总结上面的过程就是,我本地本来就有一个对象,该对象实现了所有远程的接口,至于该对象是怎么实现的,我们不用去管,最后我们去调用这个对象的接口,可以得到正确的计算结果。
所以说,IBase其实就是业务逻辑,因为IBase的子孙对象就是正真实现被调用的方法的对象。
2.3 对binder与base的理解
上面说了,IBase对象是实现的业务逻辑,也就是本地调用的一个真实的对象,那么既然要能与远程的线程进行通信就必须有一个通信的模块,这个模块就是IBinder对象。IBinder对象中最关键的方法就是transact()
,该函数会调用IPCThreadState::self()->transact()
来完成最终的消息发送过程。
IBase对象通过remote()
方法来获取与之绑定的IBinder对象,此IBinder对象在IBase对象创建时作为参数传入。这里以BpHwServiceManager
为例:
相关的文件:BpHwServiceManager.h/serviceManagerAll.cpp
BpHwServiceManager
继承自BpInterface<IServiceManager>
,BpInterface<IServiceManager>
又继承自:
template<typename INTERFACE>
class BpInterface : public INTERFACE, public IInterface, public BpHwRefBase
所以,BpHwServiceManager
继承自IServiceManager
、BpInterface
、IInterface
以及BpHwRefBase
,其中BpHwRefBase
是在IBinder.h所定义的,在前面提到过。
关于BpHwServiceManager
中mRemote的来历:
BpHwServiceManager
是在函数defaultServiceManager()
中创建的:
sp<IServiceManager> defaultServiceManager() {
{
……
while (details::gDefaultServiceManager == NULL) {
details::gDefaultServiceManager =
fromBinder<IServiceManager, BpHwServiceManager, BnHwServiceManager>(
ProcessState::self()->getContextObject(NULL));
……
}
}
}
return details::gDefaultServiceManager;
}
其中getContextObject()
最终会返回一个BpHwBinder
类型的对象:
sp<IBinder> ProcessState::getContextObject(const sp<IBinder>& /*caller*/)
{
return getStrongProxyForHandle(0);
}
sp<IBinder> ProcessState::getStrongProxyForHandle(int32_t handle)
{
……
if (e != NULL) {
……
if (b == NULL || !e->refs->attemptIncWeak(this)) {
b = new BpHwBinder(handle);
……
result = b;
} else {
……
}
}
return result;
}
接着进入fromBinder函数:
template <typename IType, typename ProxyType, typename StubType>
sp<IType> fromBinder(const sp<IBinder>& binderIface) {
……
if (binderIface->localBinder() == nullptr) {
return new ProxyType(binderIface);
}
……
}
这里实际上就是实例化了一个BpHwServiceManager
对象:
/* serviceManagerAll.cpp */
BpHwServiceManager::BpHwServiceManager(const ::android::sp<::android::hardware::IBinder> &_hidl_impl)
: BpInterface<IServiceManager>(_hidl_impl),
::android::hardware::details::HidlInstrumentor("android.hidl.manager@1.0", "IServiceManager") {
}
/* IInterface.h */
template<typename INTERFACE>
inline BpInterface<INTERFACE>::BpInterface(const sp<IBinder>& remote)
: BpHwRefBase(remote)
{
}
最终,mRemote成员被赋值为在getStrongProxyForHandle()
中创建的BpHwBinder
类型对象。
3 hardware中的toBinder与formBinder
toBinder
和formBinder
在功能上基本上是相反的两个函数,而且经常会成对的使用,所以这里就一起来分析一下。这里的to和form的对象一般会是一个IBase对象,也就是说这两个函数的意思大多数情况下(就目前我所涉及到的地方)是指的把一个IBase对象封装进一个IBinder类型的对象和通过一个IBinder类型的对象得到一个IBase类型的对象。
这里先从简单的formBinder
说起,该函数是从IBinder对象得到IBase对象的过程:
template <typename IType, typename ProxyType, typename StubType>
sp<IType> fromBinder(const sp<IBinder>& binderIface) {
using ::android::hidl::base::V1_0::IBase;
using ::android::hidl::base::V1_0::BnHwBase;
if (binderIface.get() == nullptr) {
return nullptr;
}
/* 通过localBinder来判断是Bp还是Bn */
if (binderIface->localBinder() == nullptr) {
/* 如果是Bp,则根据传入的类型创建一个对象,传入的ProxyType类型一般是Bpxxx,继承自IBase
该IBase类在创建时把传入的参数binderIface(为IBinder类型)作为remote,使IBase与IBinder
绑定 */
return new ProxyType(binderIface);
}
/* 如果是Bn类型,则binderIface对象(BHwBinder类型)创建时已经有绑定的IBase对象了,这里直接获取 */
sp<IBase> base = static_cast<BnHwBase*>(binderIface.get())->getImpl();
/* 检查该IBase对象是否可以转换成IType类型的对象,说的明白一点,其实这里就是判断
模板那里传进来的第一个参数和参数列表传进来的那个binder对象中绑定的IBase对象
是不是有关系的,一般这种关系是继承关系,如果有关系,则继续后面的操作,
如果没关系则返错,告诉调用者你传错参数了
这里,必须保证模板传进来的第一个参数和第三个参数是有关系的,一般来说第三个参数必须继承自
第一个参数,第一个参数继承自IBase */
if (details::canCastInterface(base.get(), IType::descriptor)) {
/* 这里就是一个类型转换,前面已经保证了可以转换 */
StubType* stub = static_cast<StubType*>(binderIface.get());
/* 返回一个IBase对象,该对象的类型为StubType,并继承自IType */
return stub->getImpl();
} else {
return nullptr;
}
}
接下来是toBinder
,toBinder
就是从IBase对象得到IBinder对象的过程:
template <typename IType, typename ProxyType>
sp<IBinder> toBinder(sp<IType> iface) {
IType *ifacePtr = iface.get();
if (ifacePtr == nullptr) {
return nullptr;
}
/* 通过IBase的isRemote()来判断该IBase对象是Bp还是Bn,Bp会重写该函数为true,
bn则重写该函数为false,Ixxx接口里面重写该函数为false,IBase也为false */
if (ifacePtr->isRemote()) {
/* 如果是Bp的话,BpHwBinder在创建时会传入一个IBinder对象,这里将返回创建时
传入的IBinder对象 */
return ::android::hardware::IInterface::asBinder(static_cast<ProxyType *>(ifacePtr));
} else {
/* 获取该IBase对象的描述符,这里一般在Ixxx接口中,上面的例子里此处在
ServiceManagerAll.cpp中,相关函数定义也在里面 */
std::string myDescriptor = details::getDescriptor(ifacePtr);
if (myDescriptor.empty()) {
// interfaceDescriptor fails
return nullptr;
}
/* 这里实际是获取了new BnHwServiceManager()的方法 */
auto func = details::gBnConstructorMap.get(myDescriptor, nullptr);
if (!func) {
return nullptr;
}
/* 创建一个BnHwServiceManager类型对象,其继承关系:
BnHwServiceManager-->BnHwBase-->BHwBinder-->IBinder
所以可以转换成一个IBinder对象,并返回 */
return sp<IBinder>(func(static_cast<void *>(ifacePtr)));
}
}
在上面的例子中有两个很关键的对象:BpHwServiceManager
和BnHwServiceManager
。这两个对象都是通过*.hal文件自动生成的,这里盗一张图来说明:
里面的BpFoo.h和BnFoo.h就是和上面两个类相对应的,FooAll.cpp是iFoo、BpFoo和BnFoo的实现。
但是这里有一个有趣的地方,BpHwServiceManager
是一个IBase
类型的对象,BnHwServiceManager
是一个IBinder
类型的对象,这里很容易引起误解,因为这两个类的定位看上去很对称,而且名字也很像,但结果确实完全不同的东西……这两个类的类型的不同也可以从toBinder和formBinder两个函数里窥见一二。
关于这里,在后面的camera相关代码分析的时候还会进一步分析,但是关于Google为什么这么设计,暂时还没有更深刻的理解。虽然说Bp端属于远程调用,面对的是Ixxx接口,Bn端的Bn会通过binder发送,而binder只能传输IBinder类型的对象等等,我觉得这些都不足以回答上面的疑问,我觉得这些都只是表象,Google真正这样设计的目的应该还要更往下挖几层。
4 HIDL的套路
总结一下个人理解的treble框架下的Binder通信过程:
1、Bn端向Binder server把自己注册成一个server
2、Bp端通过Ixxx接口中的getserver()方法获取一个BpHwxxx对象,该对象继承自Ixxx。这里的所谓getserver其实最终还是自己在本地创建了一个BpHwxxx对象,只是通过binder机制找到binder server获取了一些Bn端的标志信息。
3、Bp调用Ixxx的某个接口,该接口其实是在BpHwxxx中实现的,所以实际上是调用的上面创建BpHwxxx本地对象的对应接口,然后该接口中通过remote()发送一条消息给Bn端
4、Bn端收到消息后,根据消息标志进行分类,然后调用BnHwxxx中的IBase对象的对应接口,这个IBase对象必须继承Ixxx接口,并且是在BnHwxxx创建时传给BnHwxxx的。
5、上面说的BnHwxxx中的IBase对象其实就是这个接口真实执行函数,该函数执行完后,将计算结果放在输入的保存输出结果的变量中,然后该输出结果变量会一层层返回,直到到达Bp端的调用处。
以上就是远程调用的流程,也是hw binder的通信过程,所以这里面其实根本就不是什么远程调用,纯粹的消息交互,客户端给服务端一条请求,服务端计算,然后将计算结果返回给客户端,仅此而已。
关于注册binder服务的理解:
什么要registerAsServer,为什么要getServer,目前的理解为,由于不同的进程互相之间最开始完全是不通的,必须有一边先把自己注册成一个server,然后提供一个Ixxxx的接口对象,这样另外的线程在需要与其通信时就可以通过Ixxxx::getserver拿到他的接口对象的标志信息,然后才可以进行通信。那么一个线程中其实可以有多个Ixxx接口对象,每个Ixxx接口对象通过一对Bnxxx和Bpxxx进行通信,但是所有其他的Ixxxx接口对象的获取还是要依赖最初注册为Server的那个Ixxx接口对象来完成,也就是说注册成Server的Ixxx接口对象要提供创建其余Ixxx接口对象的方法。