鸿蒙子系统解读-分布式任务调度篇(下)
鸿蒙子系统解读-分布式任务调度篇(上)链接:https://harmonyos.51cto.com/posts/1953
3.1.2.协议报文的接受和解析
前面在初始化过程中,我们介绍过在轻量设备分布式调度的Feature初始化后,会在软总线发布。发布成功后,调用软总线的CreateSessionServer()注册协议处理的回调函数。回调函数总共有3个,分别如下:
我们先看一下OnSessionOpened()和OnSessionClosed(),代码如下:
上面代码显示在会话打开和关闭的时候,基本什么都没做,只是打印了log。这里的会话我理解是智慧屏端设备通过软总线与轻量设备建立的会话。
下面我们分析下最核心的函数OnBytesReceived()。这个函数应该是在会话有数据传输后被软总线回调的。
在OnBytesReceived()函数中,先用参数data和 dataLen构造了临时对象interInfo。这个对象中持有了软总线接收到的会话数据和长度,然后调用了DmsLiteProcessCommuMsg() 函数处理会话数据,并且设置了回调函数。回调函数如下定义:
这里的onStartAbilityDone()应该就是完成拉起上层应用的FA后的回调。在下一节中我们详细介绍。
我们继续看DmsLiteProcessCommuMsg()函数的实现,如下:
我们把DmsLiteProcessCommuMsg()函数的实现分为三部分:
A、进行参数校验和进程校验。CanCall是用户校验,通过getuid()函数取得当前用户ID,并判断是否是合法的用户。这里应该是一个初步的安全校验。
B、对会话报文数据进行解码
这部分代码主要是先通过调用DecodeDmsTlv()函数解析会话报文数据,然后调用Feature的回调函数onTlvParseDone()(前面我们看到这个回调并没有设置,所以这里不会调用)。
DecodeDmsTlv()函数总共3个参数,前两个参数是会话报文的数据和长度,最后一个是输出结构体TlsDmsMsgInfo,定义如下:
commandId:智慧屏发送给轻量设备的命令字,例如:DMS_MSG_CMD_START_FA(目前的代码好像只定义了这一个命令字,后续应该会扩展)
calleeBundleName:轻量设备端接收命令的包名,也就是要拉起的FA的包名
calleeAbilityName:轻量设备端接收命令的Ability名,也就是要拉起FA后显示的Ability的名字
callerSignature:智慧屏设备端发起者的签名,用于安全检查
我们再来分析下DecodeDmsTlv()函数的实现,如下:
①调用TlvBytesToNode()将报文转化为Node数据格式,并且返回Node的头指针。这里不做详细代码分析了,有兴趣的同学可以自己研究下协议解析的代码。
②调用ReadTlvNode()读取Node的数据到输出结构dmsMsg。这里不做详细代码分析了,有兴趣的同学可以自己研究下协议解析的代码。
C、根据解码得到的命令字,执行相应的动作。这里从代码上看只支持一个动作:START_FA。下一节详细介绍拉起FA的过程。
总结:
1、会话数据的处理和协议的解析处理在dmslite_session.c,dmslite_msg_parser.c,dmslite_tlv_common.c中。
2、涉及模块:
a)Softbus_lite:./foundation/communication/services/softbus_lite
3、会话数据处理和协议解析的时序图如下:
3.1.3.轻量设备端拉起FA
在前面的分析中,我们看到会话报文通过协议格式的解析最终得到一个TlvDmsMsgInfo结构体,这个结构体中包含了命令字、包名、Ability名、调用者的签名等信息。然后在DmsLiteProcessCommuMsg()函数中根据命令字执行相应的动作。目前的代码只支持一个动作,就是拉起FA。下面的是拉起FA的代码:
⑴调用CheckRemotePermission()函数检查调用者(智慧屏端)是否有权限这样做。
CheckRemotePermission()函数位于dmslite_check_remote_permission.c中。代码如下:
1、从包管理器中得到BundleInfo
这段代码首先判断uid,由于我们是在foundation进程中,用的也是FOUNDATION_UID,因此判断的第一个条件通过,是属于进程内调用。否则如果是从Shell程序执行的话,将是跨进程调用。
我们再来看看GetBmsInterface()函数的实现:
这个函数首先通过 SAMGR_GetInstance()拿到samgr的实例(前面有过介绍),然后通过GetFeatureApi()函数得到BMS_SERVICE的FeatureApi的iUnknown接口指针。再通过iUnknown->QueryInterface()拿到bmsInterface指针(这个指针里面是BmsServerProxy结构体含有这个Feature的一些API接口指针)。这个实现有点类似Android的Binder接口实现,是在服务框架子系统中实现的。BMS_SERVICE这个服务在./foundation/appexecfwk/services/bundlemgr_lite/src/bundle_ms_feature.cpp代码中注册的,有兴趣的同学可以去看下。从名字上看bundlemgr_lite应该是包管理器子系统。
我们再回到CheckRemotePermission()函数中,下面的代码是bmsInterface->GetBundleInfo(…)。这句代码是意思是通过包管理服务提供的GetBundleInfo函数查询轻量设备端的包的信息,这个包信息放在了bundleInfo结构体中。
2、检查签名是否一致
bundleInfo.appId根据注释应该是一个子串,包含了:bundleName + “_” + signature。因此bundleInfo.appId + strlen( calleeBundleName) + 1 就是bundleInfo.appId中的签名的子串位置。
然后用这个签名与发送过来的消息中的签名做一个字符串的比较,看看调用者的签名是否正确。
⑵调用StartAblitiyFromRemote()函数拉起轻量设备端的FA。
在签名检查通过后,DmsLiteProcessCommuMsg()函数中最后调用StartAbilityFromRemote()函数执行拉起FA的功能。
我们来看看StartAbilityFromRemote()函数的实现:
这段代码分为两个部分:
①注册拉起FA后的回调函数
RegisterIpcCallback()的函数实现在./foundation/communication/frameworks/ipc_lite/liteipc/src/liteipc_adapter.c中,有兴趣的同学可以看看源码。它的功能是向ipc_lite子模块注册一个回调。当Java层的FA被拉起后,应该会通过ipc机制通知native层,这里就是回调注册的地方。我们看到注册的回调是AmsResultCallback()函数。我们看下这个函数的实现:
所以这个函数的重点就是调用了g_onStartAbilityDone()函数指针,而这个函数指针的值就是在StartAbilityFromRemote()中设置的。
②调用StartAbilityFromRemoteInner()函数实现拉起FA的功能
我们看一下StartAbilityFromRemoteInner()函数的实现:
我们把这个函数分为两个部分:
A、构造一个Want结构。Want结构我感觉可以类比成Android里面的Intent。用于在不同Ability之间传输信息。我们在最前面有介绍过在智慧屏端构造Want结构,发起一个分布式调用的代码。这里的Want可以理解成从智慧屏端发送过来的Want,经过网络的传输(TCP传输)后,在轻量设备侧重新组装成Want结构体。里面包含了要拉起的FA的包名,ability名称。
我们来看下FillWant()函数的实现:
这个函数的实现首先是构造Element,将包名(bundleName)和ability名都放入到element中,然后将Element放入到Want里面,再设置Want的Identity。
B、通过samgr拿到ams的FeatureApi接口,然后通过这个接口调用启动FA。
我们看一下GetAmsInterface()函数的实现:
与前面介绍的检查签名时调用的GetBmsInterface()函数类似,都是通过samgr查询AMS_SERVICE服务的FEATURE,然后通过iUnknown接口的QueryInterface()获得AmsInterface接口。AMS_SERVICE的实现代码在./foundation/aafwk/services/abilitymgr_lite目录下,有兴趣的同学可以看看代码。
拿到amsInterface接口后,最后通过调用amsInterface->StartAbility(&want);来完成拉起FA的任务。
总结:
1、拉起FA的功能主要在dmslite_check_remote_permission.c、dmslite_famgr.c。
2、另外涉及到以下模块:
a)bundlemgr_lite:./foundation/appexecfwk/services/bundlemgr_lite
b)ipc_lite:./foundation/communication/frameworks/ipc_lite
c)abilitymgr_lite:./foundation/aafwk/services/abilitymgr_lite
3、完整的时序图如下:
鸿蒙源码的分布式调度(c语言实现的)和应用开发时候用到的分布式API(如startAbility, continueAbility)等有什么关系?