1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #define LOG_TAG "SoftBusClient"
17 #include "communicator_context.h"
18 #include "device_manager_adapter.h"
19 #include "kvstore_utils.h"
20 #include "log_print.h"
21 #include "softbus_error_code.h"
22 #include "softbus_client.h"
23
24 namespace OHOS::AppDistributedKv {
25 using namespace OHOS::DistributedKv;
26 using DmAdapter = OHOS::DistributedData::DeviceManagerAdapter;
27 using Context = DistributedData::CommunicatorContext;
SoftBusClient(const PipeInfo & pipeInfo,const DeviceId & deviceId,const std::function<int32_t (int32_t)> & getConnStatus)28 SoftBusClient::SoftBusClient(const PipeInfo &pipeInfo, const DeviceId &deviceId,
29 const std::function<int32_t(int32_t)> &getConnStatus)
30 : pipe_(pipeInfo), device_(deviceId), getConnStatus_(getConnStatus)
31 {
32 mtu_ = DEFAULT_MTU_SIZE;
33 }
34
~SoftBusClient()35 SoftBusClient::~SoftBusClient()
36 {
37 if (connId_ > 0) {
38 CloseSession(connId_);
39 }
40 }
41
operator ==(int32_t connId) const42 bool SoftBusClient::operator==(int32_t connId) const
43 {
44 return connId_ == connId;
45 }
46
operator ==(const std::string & deviceId) const47 bool SoftBusClient::operator==(const std::string &deviceId) const
48 {
49 return device_.deviceId == deviceId;
50 }
51
RestoreDefaultValue()52 void SoftBusClient::RestoreDefaultValue()
53 {
54 connId_ = INVALID_CONNECT_ID;
55 status_ = ConnectStatus::DISCONNECT;
56 routeType_ = RouteType::INVALID_ROUTE_TYPE;
57 strategy_ = Strategy::DEFAULT;
58 mtu_ = DEFAULT_MTU_SIZE;
59 }
60
GetMtuSize() const61 uint32_t SoftBusClient::GetMtuSize() const
62 {
63 ZLOGD("get mtu size connId:%{public}d mtu:%{public}d", connId_, mtu_);
64 return mtu_;
65 }
66
Send(const DataInfo & dataInfo,uint32_t totalLength)67 Status SoftBusClient::Send(const DataInfo &dataInfo, uint32_t totalLength)
68 {
69 std::lock_guard<std::mutex> lock(mutex_);
70 auto result = OpenConnect(totalLength);
71 if (result != Status::SUCCESS) {
72 return result;
73 }
74
75 ZLOGD("send data connId:%{public}d, data size:%{public}u, total length:%{public}u.",
76 connId_, dataInfo.length, totalLength);
77 int32_t ret = SendBytes(connId_, dataInfo.data, dataInfo.length);
78 if (ret != SOFTBUS_OK) {
79 ZLOGE("send data to connId%{public}d failed, ret:%{public}d.", connId_, ret);
80 return Status::ERROR;
81 }
82
83 if (routeType_ == RouteType::WIFI_P2P) {
84 UpdateP2pFinishTime(connId_, dataInfo.length);
85 }
86 return Status::SUCCESS;
87 }
88
OpenConnect(uint32_t totalLength)89 Status SoftBusClient::OpenConnect(uint32_t totalLength)
90 {
91 strategy_ = CommunicationStrategy::GetInstance().GetStrategy(device_.deviceId);
92 if (status_ == ConnectStatus::CONNECT_OK) {
93 status_ = ConnectStatus::DISCONNECT;
94 auto result = SwitchChannel(totalLength);
95 if (result == Status::SUCCESS) {
96 status_ = ConnectStatus::CONNECT_OK;
97 }
98 return result;
99 }
100
101 auto result = CreateChannel(totalLength);
102 if (result != Status::SUCCESS) {
103 return result;
104 }
105 status_ = ConnectStatus::CONNECT_OK;
106 return Status::SUCCESS;
107 }
108
SwitchChannel(uint32_t totalLength)109 Status SoftBusClient::SwitchChannel(uint32_t totalLength)
110 {
111 if (strategy_ == Strategy::BUTT) {
112 return Status::NETWORK_ERROR;
113 }
114
115 if (strategy_ == Strategy::ON_LINE_SELECT_CHANNEL) {
116 return Status::SUCCESS;
117 }
118
119 if (routeType_ == RouteType::WIFI_STA) {
120 return Status::SUCCESS;
121 }
122
123 if (routeType_ == RouteType::BT_BLE || routeType_ == RouteType::BT_BR) {
124 if (totalLength < P2P_SIZE_THRESHOLD) {
125 return Status::SUCCESS;
126 }
127
128 ZLOGD("switch %{public}s,session:%{public}s,connId:%{public}d,routeType:%{public}d to wifi or p2p.",
129 KvStoreUtils::ToBeAnonymous(device_.deviceId).c_str(), pipe_.pipeId.c_str(), connId_, routeType_);
130 RestoreDefaultValue();
131 return Open(GetSessionAttribute(true));
132 }
133
134 if (routeType_ == RouteType::WIFI_P2P) {
135 if (totalLength > P2P_SIZE_THRESHOLD * SWITCH_DELAY_FACTOR) {
136 return Status::SUCCESS;
137 }
138
139 ZLOGD("switch %{public}s,session:%{public}s,connId:%{public}d,routeType:%{public}d to ble or br.",
140 KvStoreUtils::ToBeAnonymous(device_.deviceId).c_str(), pipe_.pipeId.c_str(), connId_, routeType_);
141 {
142 std::lock_guard<std::mutex> lock(taskMutex_);
143 if (closeP2pTaskId_ == ExecutorPool::INVALID_TASK_ID) {
144 closeP2pTaskId_ = Context::getInstance().
145 GetThreadPool()->Execute(std::bind(&SoftBusClient::CloseP2pSessions, this));
146 }
147 }
148 RestoreDefaultValue();
149 return Open(GetSessionAttribute(false));
150 }
151
152 return Status::NETWORK_ERROR;
153 }
154
CreateChannel(uint32_t totalLength)155 Status SoftBusClient::CreateChannel(uint32_t totalLength)
156 {
157 if (strategy_ == Strategy::BUTT) {
158 return Status::NETWORK_ERROR;
159 }
160
161 if (strategy_ == Strategy::ON_LINE_SELECT_CHANNEL) {
162 return Open(GetSessionAttribute(true));
163 }
164
165 if (totalLength < P2P_SIZE_THRESHOLD) {
166 return Open(GetSessionAttribute(false));
167 }
168 return Open(GetSessionAttribute(true));
169 }
170
Open(SessionAttribute attr)171 Status SoftBusClient::Open(SessionAttribute attr)
172 {
173 int id = OpenSession(pipe_.pipeId.c_str(), pipe_.pipeId.c_str(),
174 DmAdapter::GetInstance().ToNetworkID(device_.deviceId).c_str(), "GROUP_ID", &attr);
175 ZLOGI("open %{public}s,session:%{public}s,connId:%{public}d,linkNum:%{public}d,strategy:%{public}d",
176 KvStoreUtils::ToBeAnonymous(device_.deviceId).c_str(), pipe_.pipeId.c_str(), id, attr.linkTypeNum, strategy_);
177 if (id < 0) {
178 ZLOGW("Open %{public}s, type:%{public}d failed, connId:%{public}d",
179 pipe_.pipeId.c_str(), attr.dataType, id);
180 return Status::NETWORK_ERROR;
181 }
182
183 connId_ = id;
184 int state = getConnStatus_(connId_);
185 ZLOGI("waited for notification, state:%{public}d connId:%{public}d", state, id);
186 if (state != SOFTBUS_OK) {
187 ZLOGE("open callback result error");
188 return Status::NETWORK_ERROR;
189 }
190
191 int32_t routeType = RouteType::INVALID_ROUTE_TYPE;
192 auto ret = GetSessionOption(connId_, SESSION_OPTION_LINK_TYPE, &routeType, sizeof(routeType));
193 if (ret != SOFTBUS_OK) {
194 ZLOGE("get routeType failed, session:%{public}s, connId:%{public}d", pipe_.pipeId.c_str(), connId_);
195 return Status::NETWORK_ERROR;
196 }
197 routeType_ = routeType;
198
199 UpdateMtuSize();
200 ZLOGI("open %{public}s, session:%{public}s success, connId:%{public}d, routeType:%{public}d",
201 KvStoreUtils::ToBeAnonymous(device_.deviceId).c_str(), pipe_.pipeId.c_str(), connId_, routeType_);
202 return Status::SUCCESS;
203 }
204
GetSessionAttribute(bool isP2p)205 SessionAttribute SoftBusClient::GetSessionAttribute(bool isP2p)
206 {
207 SessionAttribute attr;
208 attr.dataType = TYPE_BYTES;
209 // If the dataType is BYTES, the default strategy is wifi_5G > wifi_2.4G > BR, without P2P;
210 if (!isP2p) {
211 return attr;
212 }
213
214 int index = 0;
215 attr.linkType[index++] = LINK_TYPE_WIFI_WLAN_5G;
216 attr.linkType[index++] = LINK_TYPE_WIFI_WLAN_2G;
217 attr.linkType[index++] = LINK_TYPE_WIFI_P2P;
218 attr.linkType[index++] = LINK_TYPE_BR;
219 attr.linkTypeNum = index;
220 return attr;
221 }
222
UpdateMtuSize()223 void SoftBusClient::UpdateMtuSize()
224 {
225 uint32_t mtu = 0;
226 auto result = GetSessionOption(connId_, SESSION_OPTION_MAX_SENDBYTES_SIZE, &mtu, sizeof(mtu));
227 if (result != SOFTBUS_OK) {
228 return;
229 }
230 mtu_ = mtu;
231 }
232
AfterStrategyUpdate(Strategy strategy)233 void SoftBusClient::AfterStrategyUpdate(Strategy strategy)
234 {
235 std::lock_guard<std::mutex> lock(mutex_);
236 if (strategy != strategy_ && connId_ > 0) {
237 ZLOGI("close connId:%{public}d,strategy current:%{public}d, new:%{public}d", connId_, strategy_, strategy);
238 CloseSession(connId_);
239 RestoreDefaultValue();
240 }
241 }
242
CloseP2pSessions()243 void SoftBusClient::CloseP2pSessions()
244 {
245 Time now = std::chrono::steady_clock::now();
246 Time nextClose = std::chrono::steady_clock::time_point::max();
247 p2pFinishTime_.EraseIf([&now, &nextClose](const int32_t &connId, Time &finishTime) -> bool {
248 if (finishTime + P2P_CLOSE_DELAY <= now) {
249 ZLOGD("close timeout p2p connId:%{public}d", connId);
250 CloseSession(connId);
251 return true;
252 }
253
254 if (finishTime + P2P_CLOSE_DELAY < nextClose) {
255 nextClose = finishTime + P2P_CLOSE_DELAY;
256 }
257 return false;
258 });
259
260 std::lock_guard<std::mutex> lock(taskMutex_);
261 if (!p2pFinishTime_.Empty()) {
262 closeP2pTaskId_ = Context::getInstance().GetThreadPool()->Schedule(nextClose - now,
263 std::bind(&SoftBusClient::CloseP2pSessions, this));
264 } else {
265 closeP2pTaskId_ = ExecutorPool::INVALID_TASK_ID;
266 }
267 }
268
UpdateP2pFinishTime(int32_t connId,uint32_t dataLength)269 void SoftBusClient::UpdateP2pFinishTime(int32_t connId, uint32_t dataLength)
270 {
271 Time now = std::chrono::steady_clock::now();
272 auto delay = std::chrono::microseconds(dataLength / P2P_TRANSFER_PER_MICROSECOND);
273 p2pFinishTime_.Compute(connId, [&now, &delay](const auto &id, auto &finishTime) -> bool {
274 if (finishTime < now) {
275 finishTime = now + delay;
276 } else {
277 finishTime += delay;
278 }
279 return true;
280 });
281 }
282 }