积分184 / 贡献0

提问0答案被采纳0文章7

[经验分享] OpenHarmony4.0源码解析之分布式硬件管理框架 原创 精华

深开鸿_王奎 显示全部楼层 发表于 2023-12-1 19:41:24

本文以 OpenHarmony 4.0 Release 分布式硬件管理框架 源码为基础进行分析。

作者:深开鸿-王奎

概述

分布式硬件管理框架(dhfwk)是为分布式硬件子系统提供信息管理能力的部件。dhfwk为分布式硬件子系统提供统一的分布式硬件接入、查询和使能等能力。例如分布式相机,分布式屏幕,分布式音频等部件都需要 dhfwk进行统一的管理。dhfwk的核心功能主要包括两部分:

  • 设备上线后,注册并初始化设备的分布式硬件能力,如相机,音频等。
  • 设备下线后,清理并释放分布式硬件资源。

本文将从设备上下线时对分布式硬件的处理逻辑流程入手,简要介绍 dhfwk的工作原理。下文中 设备指开发板,部件指分布式硬件,如分布式相机,麦克风等。

系统架构

dhfwk的系统架构图如下所示:

arch.png

如上图所示,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继承了类 DmInitCallbackDeviceStateCallback,并实现了`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的方法时,会先检测是否初始化完成,若为初始化会先初始化 DistributedHardwareManagerDistributedHardwareManagerFactoryDistributedHardwareManager的工厂类,其方法调用最终都会调用到 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初始化时会依次对几个核心部件进行初始化。其中 VersionInfoManagerCapabilityInfoManager隶属于 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_handlersink_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()会调用 ComponentLoaderGetAllCompTypes(),GetSink(),GetSource()接口,从相应的so中加载部件的 source_handler/sink_handler实例,然后将实例本地存储。

StartSource()/StartSink()则会调用各部件 handlerInitSource()/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初始化时调用 ComponentLoaderGetAllCompTypes(),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_taskenable_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_handlerRegisterDistributedHardware()接口完成部件使能。

完成部件使能后,设备上线的整体流程就结束了,完整调用流程可参考下面时序图。

flow.png

设备下线

DeviceManager监测到对端设备下线时,会触发 AccessManager::OnDeviceOffline()回调执行设备下线逻辑。设备下线流程上和设备上线刚好相反,首先会对所有的分布式硬件去使能。若所有的可信设备都已下线,还会触发 DistributedHardwareManager::Release()释放相关资源。

部件去使能

去使能的流程与使能相似,设备下线时会创建 offline_task,下线任务的执行与上线任务相反,首先创建 disable_task对部件去使能,然后调用 CapabilityInfoManager::RemoveCapabilityInfoInMem()方法移除记录的硬件能力信息。

disable_task调用 ComponentManager::Disable()接口,然后由去使能模块 ComponentDisableDisable()接口调用 source_handlerUnregisterDistributedHardware()接口完成去使能。该操作主要是从系统中移除分布式硬件。

资源释放

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()正好相反的顺序释放组件资源。

组件的释放流程大体和初始化相反,如 LocalHardwareManagerVersionManager会清除本地记录的硬件及版本信息,CapabilityInfoManagerVersionInfoManager会释放 DBAdapter实例。这里我们不在详细介绍,下面简单介绍一下 ComponentManagerComponentLoader的释放流程。

1.ComponentManager释放

从前文初始化阶段我们知道,ComponentManager初始化时会获取并记录 source_handlersink_handler,然后调用它们的 InitSource()InitSink()接口进行sa及虚拟驱动服务的加载以完成初始化。

同样地,在释放阶段,首先会调用 source_handlersink_handlerReleaseSource()ReleaseSink()接口。该接口主要释放部件内部地资源,如sa的unload操作(4.0版本新增),虚拟驱动服务的卸载等。

2.ComponentLoader释放

ComponentLoader的释放比较简单,主要是通过 dlclose()释放初始化阶段加载的各分布式硬件的 source_handlersink_handlerhardware_handler

小结

以上就是 dhfwk的核心工作原理,本文忽略一些具体的实现细节,从整体流程上介绍了服务启动,设备上线,设备下线的核心流程及原理。

©著作权归作者所有,转载或内容合作请联系作者

您尚未登录,无法参与评论,登录后可以:
参与开源共建问题交流
认同或收藏高质量问答
获取积分成为开源共建先驱

Copyright   ©2023  OpenHarmony开发者论坛  京ICP备2020036654号-3 |技术支持 Discuz!

返回顶部