源码分析- OpenHarmony软总线

系统 OpenHarmony
软总线依旧采用鸿蒙经典的proxy - stub 架构,接口类 ISoftBusServer,ISoftBusClient。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

基本类阐述

本次说明可能侧重在标准系统之上。

软总线依旧采用鸿蒙经典的proxy - stub 架构,接口类 ISoftBusServer,ISoftBusClient。一般来说,一些服务就一个接口类,为什么软总线会有两个呢?,我们再看看继承关系。

proxy -stub 可以参考 https://ost.51cto.com/posts/10765。

和ISoftBusServer 相关的有。

类似的 ISoftBusClient。

从上面的图中可以看出,一个stub甚至对应几个proxy,看下代码,可以看到就是proxy就是解耦,更加的职责清晰。

我们通过观察目录结构和对应的代码接口进行查看,便不难看出一二。

先看 ISoftBusClient 接口类。

namespace OHOS {
class ISoftBusClient : public IRemoteBroker {
public:
virtual ~ISoftBusClient() = default;
virtual void OnDeviceFound(const DeviceInfo *device) = 0;
virtual void OnDiscoverFailed(int subscribeId, int failReason) = 0;
virtual void OnDiscoverySuccess(int subscribeId) = 0;
virtual void OnPublishSuccess(int publishId) = 0;
virtual void OnPublishFail(int publishId, int reason) = 0;
virtual int32_t OnChannelOpened(const char *sessionName, const ChannelInfo *channel) = 0;
virtual int32_t OnChannelOpenFailed(int32_t channelId, int32_t channelType) = 0;
virtual int32_t OnChannelLinkDown(const char *networkId, int32_t routeType) = 0;
virtual int32_t OnChannelMsgReceived(int32_t channelId, int32_t channelType, const void *data,
uint32_t len, int32_t type) = 0;
virtual int32_t OnChannelClosed(int32_t channelId, int32_t channelType) = 0;
virtual int32_t OnChannelQosEvent(int32_t channelId, int32_t channelType, int32_t eventId, int32_t tvCount,
const QosTv *tvList) = 0;
virtual int32_t OnJoinLNNResult(void *addr, uint32_t addrTypeLen, const char *networkId, int retCode) = 0;
virtual int32_t OnLeaveLNNResult(const char *networkId, int retCode) = 0;
virtual int32_t OnNodeOnlineStateChanged(bool isOnline, void *info, uint32_t infoTypeLen) = 0;
virtual int32_t OnNodeBasicInfoChanged(void *info, uint32_t infoTypeLen, int32_t type) = 0;
virtual int32_t OnTimeSyncResult(const void *info, uint32_t infoTypeLen, int32_t retCode) = 0;
virtual void OnPublishLNNResult(int32_t publishId, int32_t reason);
virtual void OnRefreshLNNResult(int32_t refreshId, int32_t reason);
virtual void OnRefreshDeviceFound(const void *device, uint32_t deviceLen);
public:
DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.ISoftBusClient");
};
} // namespace OHOS

其中的接口方法就是主要的SDK中的对外接口。

再看ISoftBusServer:

namespace OHOS {
class ISoftBusServer : public IRemoteBroker {
public:
virtual ~ISoftBusServer() = default;
virtual int32_t StartDiscovery(const char *pkgName, const SubscribeInfo *info) = 0;
virtual int32_t StopDiscovery(const char *pkgName, int subscribeId) = 0;
virtual int32_t PublishService(const char *pkgName, const PublishInfo *info) = 0;
virtual int32_t UnPublishService(const char *pkgName, int publishId) = 0;
virtual int32_t SoftbusRegisterService(const char *clientPkgName, const sptr<IRemoteObject> &object) = 0;
virtual int32_t CreateSessionServer(const char *pkgName, const char *sessionName) = 0;
virtual int32_t RemoveSessionServer(const char *pkgName, const char *sessionName) = 0;
virtual int32_t OpenSession(const SessionParam *param, TransInfo *info) = 0;
virtual int32_t OpenAuthSession(const char *sessionName, const ConnectionAddr *addrInfo) = 0;
virtual int32_t NotifyAuthSuccess(int channelId) = 0;
virtual int32_t CloseChannel(int32_t channelId, int32_t channelType) = 0;
virtual int32_t SendMessage(int32_t channelId, int32_t channelType,
const void *data, uint32_t len, int32_t msgType) = 0;
virtual int32_t JoinLNN(const char *pkgName, void *addr, uint32_t addrTypeLen) = 0;
virtual int32_t LeaveLNN(const char *pkgName, const char *networkId) = 0;
virtual int32_t GetAllOnlineNodeInfo(const char *pkgName, void **info, uint32_t infoTypeLen, int *infoNum) = 0;
virtual int32_t GetLocalDeviceInfo(const char *pkgName, void *info, uint32_t infoTypeLen) = 0;
virtual int32_t GetNodeKeyInfo(const char *pkgName, const char *networkId, int key, unsigned char *buf,
uint32_t len) = 0;
virtual int32_t StartTimeSync(const char *pkgName, const char *targetNetworkId, int32_t accuracy,
int32_t period) = 0;
virtual int32_t StopTimeSync(const char *pkgName, const char *targetNetworkId) = 0;
virtual int32_t QosReport(int32_t channelId, int32_t chanType, int32_t appType, int32_t quality) = 0;
virtual int32_t PublishLNN(const char *pkgName, const void *info, uint32_t infoTypeLen);
virtual int32_t StopPublishLNN(const char *pkgName, int32_t publishId);
virtual int32_t RefreshLNN(const char *pkgName, const void *info, uint32_t infoTypeLen);
virtual int32_t StopRefreshLNN(const char *pkgName, int32_t refreshId);
virtual int32_t ActiveMetaNode(const MetaNodeConfigInfo *info, char *metaNodeId);
virtual int32_t DeactiveMetaNode(const char *metaNodeId);
virtual int32_t GetAllMetaNodeInfo(MetaNodeInfo *info, int32_t *infoNum);
public:
DECLARE_INTERFACE_DESCRIPTOR(u"OHOS.ISoftBusServer");
};

包括发现设备,发布服务,相当于这是系统自启动的一个服务。

解析一次调用链

对于proxy - client 架构,一般来说就是client调用sendRequest,server便会调用OnRemoteRequest,

我们直接从stub的方法入手分析下。

SoftBusClientStub::SoftBusClientStub()
{
memberFuncMap_[CLIENT_DISCOVERY_DEVICE_FOUND] =
&SoftBusClientStub::OnDeviceFoundInner;
memberFuncMap_[CLIENT_DISCOVERY_SUCC] =
&SoftBusClientStub::OnDiscoverySuccessInner;
memberFuncMap_[CLIENT_DISCOVERY_FAIL] =
&SoftBusClientStub::OnDiscoverFailedInner;
memberFuncMap_[CLIENT_PUBLISH_SUCC] =
&SoftBusClientStub::OnPublishSuccessInner;
memberFuncMap_[CLIENT_PUBLISH_FAIL] =
&SoftBusClientStub::OnPublishFailInner;
memberFuncMap_[CLIENT_ON_CHANNEL_OPENED] =
&SoftBusClientStub::OnChannelOpenedInner;
memberFuncMap_[CLIENT_ON_CHANNEL_OPENFAILED] =
&SoftBusClientStub::OnChannelOpenFailedInner;
memberFuncMap_[CLIENT_ON_CHANNEL_LINKDOWN] =
&SoftBusClientStub::OnChannelLinkDownInner;
memberFuncMap_[CLIENT_ON_CHANNEL_CLOSED] =
&SoftBusClientStub::OnChannelClosedInner;
memberFuncMap_[CLIENT_ON_CHANNEL_MSGRECEIVED] =
&SoftBusClientStub::OnChannelMsgReceivedInner;
memberFuncMap_[CLIENT_ON_CHANNEL_QOSEVENT] =

这里我们看到是使用不同的CODE做分发。

但是对外的接口都是c接口,c++接口中没有任何内容存储信息。这是为啥?这是为了兼容标准系统和其他系统。信息存储再统一的结构里面,然后根据不同的系统编译不同的.c或者.cpp文件。

咱们以joinLNN为例:

int32_t JoinLNN(const char *pkgName, ConnectionAddr *target, OnJoinLNNResult cb)
{
if (pkgName == NULL || target == NULL || cb == NULL) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail : params are NULL!");
return SOFTBUS_INVALID_PARAM;
}
if (CommonInit(pkgName) != SOFTBUS_OK) {
return SOFTBUS_INVALID_PARAM;
}
return JoinLNNInner(pkgName, target, cb);
}

实际调用的是joinLNNInner:

int32_t JoinLNNInner(const char *pkgName, ConnectionAddr *target, OnJoinLNNResult cb)
{
int32_t rc;
if (!g_busCenterClient.isInit) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail : join lnn not init");
return SOFTBUS_NO_INIT;
}
if (SoftBusMutexLock(&g_busCenterClient.lock) != 0) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail: lock join lnn cb list in join");
}
rc = SOFTBUS_ERR;
do {
if (FindJoinLNNCbItem(target, cb) != NULL) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail : join request already exist");
rc = SOFTBUS_ALREADY_EXISTED;
break;
}
rc = ServerIpcJoinLNN(pkgName, target, sizeof(*target));
if (rc != SOFTBUS_OK) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail : request join lnn");
} else {
rc = AddJoinLNNCbItem(target, cb);
}
} while (false);
if (SoftBusMutexUnlock(&g_busCenterClient.lock) != 0) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "fail: unlock join lnn cb list in join");
}
return rc;
}

先做了一些初始化的操作,查找当前节点是否存在。然后ServerIpcJoinLNN通信就是使用的proxy-stub侧的代码。

int32_t ServerIpcJoinLNN(const char *pkgName, void *addr, unsigned int addrTypeLen)
{
if (g_serverProxy == nullptr) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "ServerIpcJoinLNN g_serverProxy is nullptr!\n");
return SOFTBUS_ERR;
}
int ret = g_serverProxy->JoinLNN(pkgName, addr, addrTypeLen);
if (ret != 0) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "ServerIpcJoinLNN failed!\n");
return ret;
}
return SOFTBUS_OK;
}

这里的关键就是g_serverProxy->JoinLNN(pkgName, addr, addrTypeLen)。

实际调用的是:

int32_t BusCenterServerProxy::JoinLNN(const char *pkgName, void *addr, uint32_t addrTypeLen)
{
if (pkgName == nullptr || addr == nullptr) {
return SOFTBUS_ERR;
}
sptr<IRemoteObject> remote = GetSystemAbility();
if (remote == nullptr) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "remote is nullptr!");
return SOFTBUS_ERR;
}
MessageParcel data;
if (!data.WriteCString(pkgName)) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN write client name failed!");
return SOFTBUS_ERR;
}
if (!data.WriteUint32(addrTypeLen)) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN write addr type length failed!");
return SOFTBUS_ERR;
}
if (!data.WriteRawData(addr, addrTypeLen)) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN write addr failed!");
return SOFTBUS_ERR;
}
MessageParcel reply;
MessageOption option;
if (remote->SendRequest(SERVER_JOIN_LNN, data, reply, option) != 0) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN send request failed!");
return SOFTBUS_ERR;
}
int32_t serverRet = 0;
if (!reply.ReadInt32(serverRet)) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "JoinLNN read serverRet failed!");
return SOFTBUS_ERR;
}
return serverRet;
}

再看对应sub中SERVER_JOIN_LNN值去调用下面这个方法:

int32_t SoftBusServerStub::JoinLNNInner(MessageParcel &data, MessageParcel &reply)
{
const char *clientName = data.ReadCString();
if (clientName == nullptr) {
SoftBusLog(SOFTBUS_LOG_COMM, SOFTBUS_LOG_ERROR, "SoftbusJoinLNNInner read clientName failed!");
return SOFTBUS_ERR;
}
uint32_t addrTypeLen;
if (!data.ReadUint32(addrTypeLen)) {
SoftBusLog(SOFTBUS_LOG_COMM, SOFTBUS_LOG_ERROR, "SoftbusJoinLNNInner read addr type length failed!");
return SOFTBUS_ERR;
}
void *addr = (void *)data.ReadRawData(addrTypeLen);
if (addr == nullptr) {
SoftBusLog(SOFTBUS_LOG_COMM, SOFTBUS_LOG_ERROR, "SoftbusJoinLNNInner read addr failed!");
return SOFTBUS_ERR;
}
int32_t retReply = JoinLNN(clientName, addr, addrTypeLen);
if (!reply.WriteInt32(retReply)) {
SoftBusLog(SOFTBUS_LOG_COMM, SOFTBUS_LOG_ERROR, "SoftbusJoinLNNInner write reply failed!");
return SOFTBUS_ERR;
}
return SOFTBUS_OK;
}

可以看到显示读数据,然后调用JoinLNN,你发现stub这个方法为空,但是要注意到这个方法是一个虚函数

去查看它的子类SoftBusServer。

int32_t SoftBusServer::JoinLNN(const char *pkgName, void *addr, uint32_t addrTypeLen)
{
return LnnIpcServerJoin(pkgName, addr, addrTypeLen);
}

所以真正调用的是 LnnIpcServerJoin,我们看下他到底做了什么,这是真正的业务逻辑所在。

int32_t LnnIpcServerJoin(const char *pkgName, void *addr, uint32_t addrTypeLen)
{
ConnectionAddr *connAddr = (ConnectionAddr *)addr;
(void)addrTypeLen;
if (pkgName == nullptr || connAddr == nullptr) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "parameters are nullptr!\n");
return SOFTBUS_INVALID_PARAM;
}
std::lock_guard<std::mutex> autoLock(g_lock);
if (IsRepeatJoinLNNRequest(pkgName, connAddr)) {
SoftBusLog(SOFTBUS_LOG_LNN, SOFTBUS_LOG_ERROR, "repeat join lnn request from: %s", pkgName);
return SOFTBUS_ALREADY_EXISTED;
}
int32_t ret = LnnServerJoin(connAddr);
if (ret == SOFTBUS_OK) {
ret = AddJoinLNNInfo(pkgName, connAddr);
}
return ret;
}

看一下,主要是有几个部分,第一查看参数有效性,第二是不是重复节点,使用连接地址创建连接,使用包名和地址建立映射。具体的感兴趣的小伙伴可以去查看一下。

​想了解更多内容,请访问:​

​51CTO和华为官方合作共建的鸿蒙技术社区​

​https://ost.51cto.com​

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-01-12 14:45:26

鸿蒙HarmonyOS应用

2021-07-02 09:16:21

鸿蒙HarmonyOS应用

2022-03-30 15:06:25

数据传输Harmony源码分析

2021-07-01 14:21:58

鸿蒙HarmonyOS应用

2022-07-19 20:04:31

NAPI模块鸿蒙

2022-06-07 10:33:29

Camera组件鸿蒙

2023-04-26 15:36:51

WPA鸿蒙

2022-11-28 15:42:39

分布式软总线鸿蒙

2022-05-20 10:32:49

事件循环器事件队列鸿蒙

2022-06-13 14:31:02

资源调度鸿蒙

2022-04-29 14:56:40

通话应用源码剖析

2022-07-27 14:30:15

分布式数据鸿蒙

2022-01-26 15:07:04

bytrace工具OpenHarmon

2022-02-14 14:47:11

SystemUIOpenHarmon鸿蒙

2021-08-20 14:27:50

鸿蒙HarmonyOS应用

2022-06-27 13:50:31

应用界面开发鸿蒙

2022-05-17 10:42:36

reboot源码解析

2022-03-28 15:28:42

分布式软总线通讯Harmony

2022-03-21 15:02:05

Harmonyhiperf鸿蒙

2022-03-30 14:43:00

Harmony分布式软总线通讯
点赞
收藏

51CTO技术栈公众号