软总线系统本身也是基于IPC对外提供服务,即软总线运行在系统进程中,对外提供一个名为SoftBusServer的服务。
但是软总线与IPC系统存在一些区别,即软总线为双向通信系统,所以客户端进程同时会维护一个本地的名为SoftBusClient的服务。即客户端维护SoftBusServerProxy和SoftBusClientStub,软总线维护SoftBusServerStub和SoftBusClientProxy。
接口一—注册服务:SoftbusRegisterService
当客户端进程想要将自己注册到软总线时,便调用ServerIpcRegisterService函数,齐源码如下所示:
static int ServerIpcRegisterService(const char *name, const void *info)
{
if (g_serverProxy == nullptr) {
LOG_ERR("softbus server g_serverProxy is nullptr!\n");
return SOFTBUS_ERR;
}
int ret = g_serverProxy->SoftbusRegisterService(name, nullptr);
if (ret != SOFTBUS_OK) {
LOG_ERR("ServerIpcRegisterService failed!\n");
return ret;
}
LOG_INFO("softbus server register service success!\n");
return SOFTBUS_OK;
}
客户端进程使用g_serverProxy保存SoftBusServer的代理对象,其SoftbusRegisterService的函数源码如下:
int32_t SoftBusServerProxy::SoftbusRegisterService(const char *clientPkgName, const sptr<IRemoteObject>& object)
{
sptr<IRemoteObject> remote = Remote();
if (remote == nullptr) {
LOG_ERR("remote is nullptr!");
return SOFTBUS_ERR;
}
MessageParcel data;
int ret = data.WriteRemoteObject(SoftBusServerProxy::GetRemoteInstance());
if (!ret) {
LOG_ERR("SoftbusRegisterService write remote object failed!");
return SOFTBUS_ERR;
}
ret = data.WriteCString(clientPkgName);
if (!ret) {
LOG_ERR("SoftbusRegisterService write clientPkgName failed!");
return SOFTBUS_ERR;
}
MessageParcel reply;
MessageOption option;
int32_t err = remote->SendRequest(MANAGE_REGISTER_SERVICE, data, reply, option);
if (err != 0) {
LOG_ERR("SoftbusRegisterService send request failed!");
return SOFTBUS_ERR;
}
int32_t serverRet = 0;
ret = reply.ReadInt32(serverRet);
if (!ret) {
LOG_ERR("SoftbusRegisterService read serverRet failed!");
return SOFTBUS_ERR;
}
return SOFTBUS_OK;
}
由上述源码可知,客户端进程将自己的应用包名,和调用GetRemoteInstance()后获取的SoftBusClientProxy对象,一起发送给服务端进程。服务端对应的SoftbusRegisterService源码如下:
int32_t SoftBusServer::SoftbusRegisterService(const char *clientPkgName, const sptr<IRemoteObject> &object)
{
if (object == nullptr) {
LOG_ERR("RegisterService object is nullptr\n");
return SOFTBUS_ERR;
}
std::lock_guard<std::recursive_mutex> autoLock(clientObjectMapLock_);
clientObjectMap_.insert(std::pair<std::string, sptr<IRemoteObject>>(clientPkgName, object));
abilityDeath_ = sptr<IRemoteObject::DeathRecipient>(new (std::nothrow) SoftBusDeathRecipient());
if (abilityDeath_ == nullptr) {
LOG_ERR("DeathRecipient object is nullptr\n");
return SOFTBUS_ERR;
}
bool ret = object->AddDeathRecipient(abilityDeath_);
if (!ret) {
LOG_ERR("AddDeathRecipient failed\n");
return SOFTBUS_ERR;
}
return SOFTBUS_OK;
}
服务端的处理很简单,就是应用包名和对应客户端进程的SoftBusClientStub对象,保存在一个名为clientObjectMap_的键值库中。
接口二—发布服务:PublishService
该接口为客户端进程发布指定的服务。与发布该服务的设备在同一局域网内的对等设备可以根据需要发现该服务,服务通过publicId和pkgName进行标识。其函数为ServerIpcPublishService,对应的源码如下:
static int ServerIpcPublishService(const char *pkgName, const void *info)
{
if (g_serverProxy == nullptr) {
LOG_ERR("softbus server g_serverProxy is nullptr!\n");
return SOFTBUS_ERR;
}
int ret = g_serverProxy->PublishService(pkgName, info);
return ret;
}
即调用客户端的代理对象的方法PublishService,其函数源码为:
int32_t SoftBusServerProxy::PublishService(const char *pkgName, const void *info)
{
sptr<IRemoteObject> remote = Remote();
if (remote == nullptr) {
LOG_ERR("remote is nullptr!");
return SOFTBUS_ERR;
}
MessageParcel data;
PublishInfo *pubInfo = (PublishInfo *)info;
data.WriteCString(pkgName);
data.WriteInt32(pubInfo->publishId);
data.WriteInt32(pubInfo->mode);
data.WriteInt32(pubInfo->medium);
data.WriteInt32(pubInfo->freq);
data.WriteCString(pubInfo->capability);
data.WriteCString((char *)pubInfo->capabilityData);
data.WriteUint32(pubInfo->dataLen);
MessageParcel reply;
MessageOption option;
int32_t err = remote->SendRequest(SERVER_PUBLISH_SERVICE, data, reply, option);
LOG_ERR("PublishService send request ret = %d!", err);
if (err != 0) {
LOG_ERR("PublishService send request failed!");
return SOFTBUS_ERR;
}
int32_t serverRet = 0;
int32_t ret = reply.ReadInt32(serverRet);
if (!ret) {
LOG_ERR("PublishService read serverRet failed!");
return SOFTBUS_ERR;
}
return serverRet;
}
由代码可知,客户端将包名和服务相关的信息一起发送到客户端,服务信息保存在PublishInfo结构体中,其主要成员变量包括publishId,mode(主动发现/被动发现),medium(蓝牙/USB/COAP),freq(慢速/中速/快速/超高速),capability(HiCall/HomeVisionPic)等。
服务端手段对应的消息后,调用PublishServiceInner处理该请求,源码如下:
int32_t SoftBusServerStub::PublishServiceInner(MessageParcel &data, MessageParcel &reply)
{
PublishInfo pubInfo;
const char *pkgName = data.ReadCString();
pubInfo.publishId = data.ReadInt32();
pubInfo.mode = (DiscoverMode)data.ReadInt32();
pubInfo.medium = (ExchanageMedium)data.ReadInt32();
pubInfo.freq = (ExchangeFreq)data.ReadInt32();
pubInfo.capability = data.ReadCString();
pubInfo.capabilityData = (unsigned char *)data.ReadCString();
pubInfo.dataLen = data.ReadUint32();
int32_t retReply = PublishService(pkgName, &pubInfo);
if (!reply.WriteInt32(retReply)) {
LOG_ERR("PublishServiceInner write reply failed!");
return SOFTBUS_ERR;
}
return SOFTBUS_OK;
}
PublishServiceInner获取消息中的对象后,调用PublishService,PublishService又调用DiscIpcPublishService,DiscIpcPublishService进行权限检查后,又调用DiscPublishService,其源码为:
int32_t DiscPublishService(const char *packageName, const PublishInfo *info, const IServerPublishCallback *cb)
{
int32_t ret;
if ((packageName == NULL) || (info == NULL) || (cb == NULL)) {
return SOFTBUS_INVALID_PARAM;
}
ret = PublishInfoCheck(packageName, info);
if (ret != SOFTBUS_OK) {
goto EXIT;
}
if (g_isInited == false) {
LOG_ERR("not init");
ret = SOFTBUS_DISCOVER_MANAGER_NOT_INIT;
goto EXIT;
}
DiscInfo *infoNode = CreateNewPublishInfoNode(info);
if (infoNode == NULL) {
LOG_ERR("infoNode create failed");
ret = SOFTBUS_DISCOVER_MANAGER_INFO_NOT_CREATE;
goto EXIT;
}
ret = InnerPublishService(packageName, infoNode, cb, PUBLISH_SERVICE);
if (ret != SOFTBUS_OK) {
ReleaseInfoNodeMem(infoNode, PUBLISH_SERVICE);
goto EXIT;
}
cb->OnServerPublishSuccess(packageName, info->publishId);
return SOFTBUS_OK;
EXIT:
if (ret == SOFTBUS_DISCOVER_MANAGER_INVALID_MEDIUM) {
cb->OnServerPublishFail(packageName, info->publishId, PUBLISH_FAIL_REASON_NOT_SUPPORT_MEDIUM);
return ret;
}
cb->OnServerPublishFail(packageName, info->publishId, PUBLISH_FAIL_REASON_INTERNAL);
return ret;
}
总共分为三部分:1.创建一个内部的服务信息InfoNode,2.调用InnerPublishService,3.发布成功,调用回调函数OnServerPublishSuccess。
InnerPublishService的源码如下:
static int32_t InnerPublishService(const char *packageName, DiscInfo *info, const IServerPublishCallback *cb,
const ServiceType type)
{
int32_t ret;
InnerCallback callback;
if ((cb != NULL) &&
(memcpy_s(&(callback.publishCb), PUBLISH_CB_LEN, cb, PUBLISH_CB_LEN) != EOK)) {
LOG_ERR("memcpy_s erro");
return SOFTBUS_MEM_ERR;
}
ret = AddInfoToList(g_publishInfoList, packageName, &callback, info, type);
if (ret != SOFTBUS_OK) {
LOG_ERR("add list fail");
return ret;
}
ret = DiscInterfaceByMedium(info, PUBLISH_FUNC);
if (ret != SOFTBUS_OK) {
LOG_ERR("interface fail");
return ret;
}
return SOFTBUS_OK;
}
即将InfoNode加到系统内部维护的全局列表g_publishInfoList中,随后调用DiscInterfaceByMedium,该函数即根据InfoNode的类型(例如蓝牙或者COAP),调用对应的Publish处理。
回调函数OnServerPublishSuccess最终会调用ClientIpcOnPublishSuccess,其源码如下:
static int ClientIpcOnPublishSuccess(const char *pkgName, int publishId)
{
sptr<ISoftBusClient> clientProxy = GetClientProxy(pkgName);
if (clientProxy == nullptr) {
LOG_ERR("softbus client proxy is nullptr!\n");
return SOFTBUS_ERR;
}
clientProxy->OnPublishSuccess(publishId);
return SOFTBUS_OK;
}
ClientIpcOnPublishSucces获取SoftBusClient的代理对象后,调用其OnPublishSuccess方法。获取SoftBusClient代理的方法源码如下:
static sptr<ISoftBusClient> GetClientProxy(const char *pkgName)
{
sptr<SoftBusServer> softbusServer = SoftBusServer::GetInstance();
if (softbusServer == nullptr) {
LOG_ERR("softbusServer is nullptr!\n");
return nullptr;
}
sptr<ISoftBusClient> clientProxy = softbusServer->GetSoftbusClientProxy(pkgName);
return clientProxy;
}
其调用了SoftBusServer的函数GetSoftbusClientProxy,其源码如下:
sptr<ISoftBusClient> SoftBusServer::GetSoftbusClientProxy(const char *pkgName)
{
std::lock_guard<std::recursive_mutex> autoLock(clientObjectMapLock_);
auto iter = clientObjectMap_.find(pkgName);
if (iter != clientObjectMap_.end()) {
sptr<ISoftBusClient> clientProxy = iface_cast<ISoftBusClient>(iter->second);
if (clientProxy != nullptr) {
return clientProxy;
}
}
LOG_ERR("GetSoftbusClientProxy client proxy is nullptr\n");
return nullptr;
}
clientObjectMap_即为调用接口1:SoftbusRegisterService时,保存在服务端本地的SoftbusClientProxy对象。所以客户端应用在调用Publish发布服务时,应首先向服务端注册自己的应用包。SoftbusClientProxy的OnPublishSuccess方法源码如下:
void SoftBusClientProxy::OnPublishSuccess(int publishId)
{
sptr<IRemoteObject> remote = Remote();
if (remote == nullptr) {
LOG_ERR("remote is nullptr");
return;
}
MessageParcel data;
data.WriteInt32(publishId);
MessageParcel reply;
MessageOption option;
int32_t err = remote->SendRequest(CLIENT_PUBLISH_SUCC, data, reply, option);
if (err != 0) {
LOG_ERR("OnPublishSuccess send request failed");
return;
}
}
即将发布成功的服务对应的PubId写回到客户端应用,客户端与之对应的对象SoftbusClientStub调用方法OnPublishFailInner,起源码如下
int32_t SoftBusClientStub::OnPublishSuccessInner(MessageParcel &data, MessageParcel &reply)
{
int publishId = data.ReadInt32();
OnPublishSuccess(publishId);
return SOFTBUS_OK;
}
最终OnPublishSuccess又会调用DiscClientOnPublishSuccess,其源码如下
void DiscClientOnPublishSuccess(int32_t publishId)
{
g_discInfo->publishCb.OnPublishSuccess(publishId);
}
g_discInfo中的回调为调用AddPublishInfo时增加的回调函数。
至此,接口2:PublishService也已分析完毕
接口三—发现服务:StartDiscovery
当客户端发布了自己的某个服务后,远端设备的客户端便可以通过调用StartDiscovery来发现该设备的已发布服务。
远端设备调用ServerIpcStartDiscovery函数发现设备,其源码流程与PublishService流程一致,因此不一一赘述。
简而言之,ServerIpcStartDiscovery会调用客户端的SoftBusServerProxy的StartDiscovery函数,该函数发送请求给远端设备的服务进程,服务进程调用StartDiscoveryInner函数读取发送端填写的参数后,再调用本地的StartDiscovery方法处理该函数。StartDiscovery会调用DiscIpcStartDiscovery,其源码如下:
int32_t DiscIpcStartDiscovery(const char *packageName, const SubscribeInfo *info)
{
if (CheckDiscPermission(packageName) != true) {
LOG_ERR("ServerStartDiscovery no permission!");
return SOFTBUS_DISCOVER_SERVER_NO_PERMISSION;
}
IServerDiscoveryCallback callback;
callback.OnServerDeviceFound = (void *)(GetClientProvideInterface()->onDeviceFound);
callback.OnServerDiscoverySuccess = (void *)(GetClientProvideInterface()->onDiscoverySuccess);
callback.OnServerDiscoverFailed = (void *)(GetClientProvideInterface()->onDiscoverFailed);
int32_t ret = DiscStartDiscovery(packageName, info, &callback);
if (ret != SOFTBUS_OK) {
LOG_ERR("ServerStartDiscovery failed");
return ret;
}
LOG_INFO("ServerStartDiscovery success!");
return SOFTBUS_OK;
}
即检查验证包有效后,调用DiscStartDiscovery函数,其源码如下所示:
int32_t DiscStartDiscovery(const char *packageName, const SubscribeInfo *info, const IServerDiscoveryCallback *cb)
{
int32_t ret;
if ((packageName == NULL) || (info == NULL) || (cb == NULL)) {
return SOFTBUS_INVALID_PARAM;
}
ret = SubscribeInfoCheck(packageName, info);
if (ret != SOFTBUS_OK) {
goto EXIT;
}
if (g_isInited == false) {
LOG_ERR("not init");
ret = SOFTBUS_DISCOVER_MANAGER_NOT_INIT;
goto EXIT;
}
DiscInfo *infoNode = CreateNewSubscribeInfoNode(info);
if (infoNode == NULL) {
LOG_ERR("infoNode create failed");
ret = SOFTBUS_DISCOVER_MANAGER_INFO_NOT_CREATE;
goto EXIT;
}
ret = InnerStartDiscovery(packageName, infoNode, cb, SUBSCRIBE_SERVICE);
if (ret != SOFTBUS_OK) {
ReleaseInfoNodeMem(infoNode, SUBSCRIBE_SERVICE);
goto EXIT;
}
cb->OnServerDiscoverySuccess(packageName, info->subscribeId);
return SOFTBUS_OK;
EXIT:
if (ret == SOFTBUS_DISCOVER_MANAGER_INVALID_MEDIUM) {
cb->OnServerDiscoverFailed(packageName, info->subscribeId, DISCOVERY_FAIL_REASON_NOT_SUPPORT_MEDIUM);
return ret;
}
cb->OnServerDiscoverFailed(packageName, info->subscribeId, DISCOVERY_FAIL_REASON_INTERNAL);
return ret;
}
DiscStartDiscovery总共分为三部分:1.创建一个内部的服务信息InfoNode,2.调用InnerStartDiscovery,3.发布成功,调用回调函数OnServerDiscoverySuccess。InnerStartDiscovery的源码如下所示:
static int32_t InnerStartDiscovery(const char *packageName, DiscInfo *info, const IServerDiscoveryCallback *cb,
const ServiceType type)
{
int32_t ret;
InnerCallback callback;
if ((cb != NULL) &&
(memcpy_s(&(callback.subscribeCb), DISCOVERY_CB_LEN, cb, DISCOVERY_CB_LEN) != EOK)) {
LOG_ERR("memcpy_s erro");
return SOFTBUS_MEM_ERR;
}
if (type == SUBSCRIBE_INNER_SERVICE) {
callback.innerCb.OnDeviceFound = NULL;
}
ret = AddInfoToList(g_discoveryInfoList, packageName, &callback, info, type);
if (ret != SOFTBUS_OK) {
LOG_ERR("add list fail");
return ret;
}
ret = DiscInterfaceByMedium(info, STARTDISCOVERTY_FUNC);
if (ret != SOFTBUS_OK) {
LOG_ERR("interface fail");
return ret;
}
return SOFTBUS_OK;
}
即将创建后的Info添加到g_discoveryInfoList中,该函数即根据InfoNode的类型(例如蓝牙或者COAP),调用对应的StartAdvertise处理。
OnServerDiscoverySuccess会调用ClientIpcOnDiscoverySuccess来完成回调处理,其源码如下:
static int ClientIpcOnDiscoverySuccess(const char *pkgName, int subscribeId)
{
sptr<ISoftBusClient> clientProxy = GetClientProxy(pkgName);
if (clientProxy == nullptr) {
LOG_ERR("softbus client proxy is nullptr!\n");
return SOFTBUS_ERR;
}
clientProxy->OnDiscoverySuccess(subscribeId);
return SOFTBUS_OK;
}
与PublishService的回调一样,服务端获取SoftBusClient的代理对象后,调用其OnDiscoverySuccess方法,该方法发送请求给客户端进程,客户端解析对应的命令后调用OnDiscoverySuccess方法,该方法又会调用DiscClientOnDiscoverySuccess完成回调处理,其源码如下所示:
void DiscClientOnDiscoverySuccess(int32_t subscribeId)
{
g_discInfo->subscribeCb.OnDiscoverySuccess(subscribeId);
}
g_discInfo中的回调为调用AddSubscribeInfo时增加的回调函数。
至此,接口2:StartDiscovery也已分析完毕
本文深入分析了HarmonyOS软总线的源码,详细讲解了注册服务、发布服务和发现服务的实现流程。软总线基于IPC,采用双向通信,客户端和服务器端维护各自的SoftBusServerProxy和SoftBusClientStub。在注册服务时,客户端将包名和SoftBusClientProxy发送给服务端保存。发布服务时,客户端将服务信息发送到服务端,服务端处理并调用回调。发现服务则类似,客户端发起请求,服务端调用本地方法处理并回调。

2593





