概述
分布式硬件管理框架(dhfwk
)是为分布式硬件子系统提供信息管理能力的部件。dhfwk
为分布式硬件子系统提供统一的分布式硬件接入、查询和使能等能力。例如分布式相机,分布式屏幕,分布式音频等部件都需要 dhfwk
进行统一的管理。dhfwk
的核心功能主要包括两部分:
- 设备上线后,注册并初始化设备的分布式硬件能力,如相机,音频等。
- 设备下线后,清理并释放分布式硬件资源。
本文将从设备上下线时对分布式硬件的处理逻辑流程入手,简要介绍 dhfwk
的工作原理。下文中 设备
指开发板,部件
指分布式硬件,如分布式相机,麦克风等。
系统架构
dhfwk
的系统架构图如下所示:
如上图所示,dhfwk
主要包括以下六个部件:
ResourceManager
:对接分布式数据服务,用于存储信任体系内,本机和周边设备同步过来的设备硬件信息。VersionManager
:用于管理超级终端内,各个设备的分布式硬件平台和分布式硬件部件的版本号,供分布式硬件业务各个部件业务使用。AccessManager
:用于部件接入控制,设备上下线时触发对应的逻辑。ComponentManager
:对接各分布式硬件实例化的部件,实现对部件的动态加载和使能/去使能等操作。ComponentLoader
:用于解析部件配置文件,按需加载部件驱动的实现 so,获取驱动外部接口函数句柄以及实现版本,供其他业务使用。LocalHardwareManager
:用于采集本地硬件信息,并通过ResourceManager
进行硬件信息的持久化存储;同时,通过对接硬件驱动,用于感知本地硬件的插拔等操作,感知是否新增或移除可用硬件,将动态变化的硬件设备也纳入分布式硬件管理。
分布式硬件实质是在本地使用对端设备的硬件能力,为了抽象这一场景,我们引入**主控端
和 被控端
**的概念:
主控端(source)
:借助于具体部件的实现(如daudio
)调用被控端部件的能力,如播放音频,录音等。被控端(sink)
:通过具体的部件实现接收主控端的命令,调用本地硬件完成命令并将数据返回给主控端。
双端通信的细节由各部件内部实现,此处不再展开。
服务启动
dhfwk
对应的系统服务是 dhardware
,该服务随设备开机启动,主要负责对可信设备上下线及本地硬件设备改动进行监听并作出相应。
void DistributedHardwareService::OnStart()
{
Init();
}
bool DistributedHardwareService::Init()
{
Publish(this);
AccessManager::GetInstance()->Init();
return true;
}
通过上述代码我们可以看到,服务启动时会初始化 AccessManager
以监听可信设备的上下线。
注意:文中代码片段均经过修改,仅保留核心逻辑片段,删除日志输出,错误处理等非核心代码!
AccessManager
的初始化主要包括三步,初始化设备管理器 DeviceManager
,注册设备状态变化监听回调,以及向其他在线设备发送上线通知并处理对应的分布式硬件注册工作。
int32_t AccessManager::Init()
{
DeviceManager::GetInstance().InitDeviceManager(DH_FWK_PKG_NAME, shared_from_this());
DeviceManager::GetInstance().RegisterDevStateCallback(DH_FWK_PKG_NAME, "", shared_from_this());
SendTrustedDeviceOnline();
return DH_FWK_SUCCESS;
}
我们可以看到,上述代码将 shared_from_this()
作为参数传递给了 DeviceManager::RegisterDevStateCallback()
。这是因为 AccessManager
继承了类 DmInitCallback
和 DeviceStateCallback
,并实现了 ``OnDeviceOnline(),
OnDeviceReady(),
OnDeviceOffline()等方法以实现
DeviceManager 监听到可信设备状态变化时可以通知到
AccessManager` 并处理对应的分布式硬件上下线逻辑。
SendTrustedDeviceOnline()
方法会通过 DeviceManager
扫描在线可信设备,并触发设备上线相关逻辑。具体参考下文。
设备上线
触发设备上线逻辑共有两种情况:
- 本机服务启动,
SendTrustedDeviceOnline()
方法中扫描到了有其他在线的可信设备。 DeviceManager
监测到其他设备上线,触发AccessManager::OnDeviceReady()
回调。
void AccessManager::SendTrustedDeviceOnline()
{
std::vector<DmDeviceInfo> deviceList;
DeviceManager::GetInstance().GetTrustedDeviceList(DH_FWK_PKG_NAME, "", deviceList);
for (const auto &deviceInfo : deviceList) {
const auto networkId = std::string(deviceInfo.deviceId);
const auto uuid = GetUUIDBySoftBus(networkId);
DistributedHardwareManagerFactory::GetInstance().SendOnLineEvent(networkId, uuid, deviceInfo.deviceTypeId);
}
}
void AccessManager::OnDeviceReady(const DmDeviceInfo &deviceInfo)
{
auto networkId = std::string(deviceInfo.deviceId);
uuid = GetUUIDBySoftBus(networkId);
DistributedHardwareManagerFactory::GetInstance().SendOnLineEvent(networkId, uuid, deviceInfo.deviceTypeId);
}
上面两种情况最终都会调用 DistributedHardwareManagerFactory::GetInstance().SendOnLineEvent()
方法。DistributedHardwareManager
会对架构部分提到的六大组件进行统一的管理。
初始化
首次调用 DistributedHardwareManagerFactory
的方法时,会先检测是否初始化完成,若为初始化会先初始化 DistributedHardwareManager
。DistributedHardwareManagerFactory
是 DistributedHardwareManager
的工厂类,其方法调用最终都会调用到 DistributedHardwareManager
。
int32_t DistributedHardwareManager::Initialize()
{
VersionInfoManager::GetInstance()->Init();
ComponentLoader::GetInstance().Init();
VersionManager::GetInstance().Init();
ComponentManager::GetInstance().Init();
CapabilityInfoManager::GetInstance()->Init();
LocalHardwareManager::GetInstance().Init();
return DH_FWK_SUCCESS;
}
由上面代码我们可以看到,DistributedHardwareManager
初始化时会依次对几个核心部件进行初始化。其中 VersionInfoManager
和 CapabilityInfoManager
隶属于 ResourceManager
。这两者的初始化主要完成对应的数据库组件 DBAdapter
的初始化,这里不再详细介绍。
1.ComponentLoader
初始化
ComponentLoader
的初始化主要完成分布式硬件对应的 so 库加载。主要包括以下三种:
source_handler
:设备作为主控端时提供给dhfwk
的接口,包括部件主控端的初始化等接口。sink_handler
:设备作为被控端时提供给dhfwk
的接口,包括部件主控端的初始化等接口。hardware_handler
:部件提供给dhfwk
的接口,用于本地硬件信息的处理。
ComponentLoader
在初始化时会读取系统配置文件 /vendor/etc/distributedhardware/distributed_hardware_components_cfg.json
。该文件定义了设备当前支持的分布式硬件及其相关 so 库的路径,saId 等信息,如下样例所示:
{
"distributed_components": [
{
"name": "distributed_audio",
"type": "AUDIO",
"comp_handler_loc": "libdistributed_audio_handler.z.so",
"comp_handler_version": "1.0",
"comp_source_loc": "libdistributed_audio_source_sdk.z.so",
"comp_source_version": "1.0",
"comp_source_sa_id": 4805,
"comp_sink_loc": "libdistributed_audio_sink_sdk.z.so",
"comp_sink_version": "1.0",
"comp_sink_sa_id": 4806
},
{
"name": "distributed_camera",
"type": "CAMERA",
"comp_handler_loc": "libdistributed_camera_handler.z.so",
"comp_handler_version": "1.0",
"comp_source_loc": "libdistributed_camera_source_sdk.z.so",
"comp_source_version": "1.0",
"comp_source_sa_id": 4803,
"comp_sink_loc": "libdistributed_camera_sink_sdk.z.so",
"comp_sink_version": "1.0",
"comp_sink_sa_id": 4804
}
}
从上述文件中解析出分布式硬件对应的 so 后,调用 dlopen()
完成加载。同时,会用文件中的 version
字段初始化相应的部件版本。VersionManager
主要负责管理部件版本,初始化阶段会调用 ComponentLoader::GetLocalDHVersion()
初始化相应版本信息。
2.ComponentManager
初始化
ComponentManager
主要负责对各个部件进行管理,如初始化,使能,去使能等。其初始化会通过上一步中加载的各部件的 source_handler
和 sink_handler
调用到部件具体的初始化入口。其核心逻辑如下
int32_t ComponentManager::Init()
{
InitCompSource();
InitCompSink();
auto sourceResult = StartSource();
auto sinkResult = StartSink();
WaitForResult(Action::START_SOURCE, sourceResult);
WaitForResult(Action::START_SINK, sinkResult);
return DH_FWK_SUCCESS;
}
InitCompSource()/InitCompSink()
会调用 ComponentLoader
的 GetAllCompTypes(),GetSink(),GetSource()
接口,从相应的 so 中加载部件的 source_handler/sink_handler
实例,然后将实例本地存储。
StartSource()/StartSink()
则会调用各部件 handler
的 InitSource()/InitSink()
方法进入部件内部的相关初始化,具体的初始化包括部件相关的 sa,虚拟驱动服务等加载。如下以 StartSink()
代码为例:
ActionResult ComponentManager::StartSink()
{
std::unordered_map<DHType, std::shared_future<int32_t>> futures;
std::string uuid = DHContext::GetInstance().GetDeviceInfo().uuid;
for (const auto &item : compSink_) {
CompVersion compversion;
VersionManager::GetInstance().GetCompVersion(uuid, item.first, compversion);
auto params = compversion.sinkVersion;
auto future = std::async(std::launch::async, [item, params]() { return item.second->InitSink(params); });
futures.emplace(item.first, future.share());
}
return futures;
}
因为 InitSource()/InitSink()
的调用是通过异步任务实现的。因此还需要使用 WaitForResult()
阻塞线程等待部件初始化完成。
3.LocalHardwareManager
初始化
LocalHardwareManager
主要负责采集并存储本地硬件信息以及检测本地硬件的改动。与 source_handler/sink_handler
类似,LocalHardwareManager
初始化时调用 ComponentLoader
的 GetAllCompTypes(),GetHardwareHandler()
接口获取各部件的 hardware_handler
实例,然后调用实例的 Initialize()
接口完成初始化。
接着需要调用 hardware_handler->Query()
接口查询本地设备存在的硬件。并通过 CapabilityInfoManager::AddCapability()
接口将硬件信息存储在数据库。
最后调用 hardware_handler->IsSupportPlugin()
接口查询硬件是否支持热插拔,若支持热插拔,则调用 hardwareHandler->RegisterPluginListener()
注册硬件插拔事件监听回调。回调中监听到插拔事件后调用 CapabilityInfoManager::AddCapability()
和 CapabilityInfoManager::RemoveCapabilityInfoByKey()
接口更新存储的硬件信息。
具体代码参考以下:
void LocalHardwareManager::Init()
{
std::vector<DHType> allCompTypes = ComponentLoader::GetInstance().GetAllCompTypes();
for (auto dhType : allCompTypes) {
IHardwareHandler *hardwareHandler = nullptr;
ComponentLoader::GetInstance().GetHardwareHandler(dhType, hardwareHandler);
hardwareHandler->Initialize();
QueryLocalHardware(dhType, hardwareHandler);
if (!hardwareHandler->IsSupportPlugin()) {
ComponentLoader::GetInstance().ReleaseHardwareHandler(dhType);
hardwareHandler = nullptr;
} else {
std::shared_ptr<PluginListener> listener = std::make_shared<PluginListenerImpl>(dhType);
hardwareHandler->RegisterPluginListener(listener);
}
}
}
void LocalHardwareManager::QueryLocalHardware(const DHType dhType, IHardwareHandler *hardwareHandler)
{
std::vector<DHItem> dhItems;
dhItems = hardwareHandler->Query();
std::vector<std::shared_ptr<CapabilityInfo>> capabilityInfos;
for (auto dhItem : dhItems) {
std::shared_ptr<CapabilityInfo> dhCapabilityInfo =
std::make_shared<CapabilityInfo>(dhItem.dhId, deviceId, devName, devType, dhType, dhItem.attrs);
capabilityInfos.push_back(dhCapabilityInfo);
}
CapabilityInfoManager::GetInstance()->AddCapability(capabilityInfos);
}
部件使能
如前文所述,首次调用 DistributedHardwareManagerFactory
的方法时,会进行上述初始化流程。初始化完成后继续进行 DistributedHardwareManager::SendOnLineEvent()
的上线操作。
dhfwk
中封装了自己的 task
类,同时使用 TaskFactory
对任务进行统一管理,task
框架的实现此处不详细介绍。我们仅介绍设备上线流程中涉及到的 online_task
和 enable_task
两种任务。
如上述,DistributedHardwareManager::SendOnLineEvent()
方法中会创建一个 online_task
去完成上线流程。online_task
首先会通过分布式 KVStore
和对端设备同步版本及硬件能力信息,然后读取相关信息并本地记录。
void OnLineTask::DoSyncInfo()
{
CapabilityInfoManager::GetInstance()->ManualSync(GetNetworkId());
VersionInfoManager::GetInstance()->ManualSync(GetNetworkId());
CapabilityInfoManager::GetInstance()->SyncDeviceInfoFromDB(GetDeviceIdByUUID(GetUUID()));
VersionInfoManager::GetInstance()->SyncVersionInfoFromDB(GetDeviceIdByUUID(GetUUID()));
}
完成信息同步后 online_task
会创建使能任务 enable_task
完成部件使能,设备使能主要是完成主控端的初始化,包括创建本地适配器,在本地注册分布式硬件,使设备可以通过和使用本地硬件相同的方法使用分布式硬件。
enable_task
则通过 ComponentManager::Enable()
接口进行使能操作。ComponentManager
则调用独立的使能模块 ComponentEnable
中的 Enable
接口完成使能。该接口调用之前加载的 source_handler
的 RegisterDistributedHardware()
接口完成部件使能。
完成部件使能后,设备上线的整体流程就结束了,完整调用流程可参考下面时序图。
[图片上传失败…(image-5eb467-1724661938376)]
设备下线
当 DeviceManager
监测到对端设备下线时,会触发 AccessManager::OnDeviceOffline()
回调执行设备下线逻辑。设备下线流程上和设备上线刚好相反,首先会对所有的分布式硬件去使能。若所有的可信设备都已下线,还会触发 DistributedHardwareManager::Release()
释放相关资源。
部件去使能
去使能的流程与使能相似,设备下线时会创建 offline_task
,下线任务的执行与上线任务相反,首先创建 disable_task
对部件去使能,然后调用 CapabilityInfoManager::RemoveCapabilityInfoInMem()
方法移除记录的硬件能力信息。
disable_task
调用 ComponentManager::Disable()
接口,然后由去使能模块 ComponentDisable
的 Disable()
接口调用 source_handler
的 UnregisterDistributedHardware()
接口完成去使能。该操作主要是从系统中移除分布式硬件。
资源释放
在 DistributedHardwareManagerFactory::SendOffLineEvent()
方法中,完成设备下线任务后,如果监测到所有可信设备均已下线,则会触发资源释放流程以减少系统负载。
int32_t DistributedHardwareManagerFactory::SendOffLineEvent(const std::string &networkId, const std::string &uuid,
uint16_t deviceType)
{
DistributedHardwareManager::GetInstance().SendOffLineEvent(networkId, uuid, deviceType);
DHContext::GetInstance().RemoveOnlineDevice(uuid);
if (DistributedHardwareManager::GetInstance().GetOnLineCount() == 0) {
DHLOGI("all devices are offline, start to free the resource");
UnInit();
}
return DH_FWK_SUCCESS;
}
上述 UnInit()
方法会调用到 DistributedHardwareManager::Release()
方法,该方法释放资源的流程与 DistributedHardwareManager::Initialize()
正好相反。
int32_t DistributedHardwareManager::Release()
{
TaskBoard::GetInstance().WaitForALLTaskFinish();
LocalHardwareManager::GetInstance().UnInit();
CapabilityInfoManager::GetInstance()->UnInit();
ComponentManager::GetInstance().UnInit();
VersionManager::GetInstance().UnInit();
ComponentLoader::GetInstance().UnInit();
VersionInfoManager::GetInstance()->UnInit();
return DH_FWK_SUCCESS;
}
首先调用 TaskBoard::GetInstance().WaitForALLTaskFinish()
阻塞线程等待所有异步任务完成。然后按照和 Initialize()
正好相反的顺序释放组件资源。
组件的释放流程大体和初始化相反,如 LocalHardwareManager
和 VersionManager
会清除本地记录的硬件及版本信息,CapabilityInfoManager
和 VersionInfoManager
会释放 DBAdapter
实例。这里我们不在详细介绍,下面简单介绍一下 ComponentManager
和 ComponentLoader
的释放流程。
1.ComponentManager
释放
从前文初始化阶段我们知道,ComponentManager
初始化时会获取并记录 source_handler
和 sink_handler
,然后调用它们的 InitSource()
和 InitSink()
接口进行 sa 及虚拟驱动服务的加载以完成初始化。
同样地,在释放阶段,首先会调用 source_handler
和 sink_handler
的 ReleaseSource()
和 ReleaseSink()
接口。该接口主要释放部件内部地资源,如 sa 的 unload 操作(4.0 版本新增),虚拟驱动服务的卸载等。
2.ComponentLoader
释放
ComponentLoader
的释放比较简单,主要是通过 dlclose()
释放初始化阶段加载的各分布式硬件的 source_handler
,sink_handler
和 hardware_handler
。
小结
以上就是 dhfwk
的核心工作原理,本文忽略一些具体的实现细节,从整体流程上介绍了服务启动,设备上线,设备下线的核心流程及原理。
写在最后
有很多小伙伴不知道学习哪些鸿蒙开发技术?不知道需要重点掌握哪些鸿蒙应用开发知识点?而且学习时频繁踩坑,最终浪费大量时间。所以有一份实用的鸿蒙(OpenHarmony)资料用来跟着学习是非常有必要的。
想更深入的学习 OpenHarmony (鸿蒙南向)全栈开发的内容可点击领取一下学习文档,限时开源,先到先得~无套路领取!!
请点击→《鸿蒙南向开发学习路线》
OpenHarmony入门学习视频
鸿蒙南向开发实战教学视频
获取以上完整鸿蒙HarmonyOS学习文档,请点击→《鸿蒙南向开发学习路线》
《OpenHarmony源码解析》:
- 搭建开发环境
- Windows 开发环境的搭建
- Ubuntu 开发环境搭建
- Linux 与 Windows 之间的文件共享
- ……
系统架构分析:
- 构建子系统
- 启动流程
- 子系统
- 分布式任务调度子系统
- 分布式通信子系统
- 驱动子系统
- ……