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 #include "soft_bus_channel.h"
16
17 #include <securec.h>
18
19 #include "constant_common.h"
20 #include "device_info_manager.h"
21 #include "token_sync_event_handler.h"
22 #include "token_sync_manager_service.h"
23 #include "singleton.h"
24 #include "soft_bus_manager.h"
25
26 namespace OHOS {
27 namespace Security {
28 namespace AccessToken {
29 namespace {
30 static constexpr OHOS::HiviewDFX::HiLogLabel LABEL = {LOG_CORE, SECURITY_DOMAIN_ACCESSTOKEN, "SoftBusChannel"};
31 }
32 namespace {
33 static const std::string REQUEST_TYPE = "request";
34 static const std::string RESPONSE_TYPE = "response";
35 static const std::string TASK_NAME_CLOSE_SESSION = "atm_soft_bus_channel_close_session";
36 static const int32_t EXECUTE_COMMAND_TIME_OUT = 3000;
37 static const int32_t WAIT_SESSION_CLOSE_MILLISECONDS = 5 * 1000;
38 // send buf size for header
39 static const int RPC_TRANSFER_HEAD_BYTES_LENGTH = 1024 * 256;
40 // decompress buf size
41 static const int RPC_TRANSFER_BYTES_MAX_LENGTH = 1024 * 1024;
42 } // namespace
SoftBusChannel(const std::string & deviceId)43 SoftBusChannel::SoftBusChannel(const std::string &deviceId)
44 : deviceId_(deviceId), mutex_(), callbacks_(), responseResult_(""), loadedCond_()
45 {
46 ACCESSTOKEN_LOG_DEBUG(LABEL, "SoftBusChannel(deviceId)");
47 isDelayClosing_ = false;
48 session_ = Constant::INVALID_SESSION;
49 isSessionUsing_ = false;
50 }
51
~SoftBusChannel()52 SoftBusChannel::~SoftBusChannel()
53 {
54 ACCESSTOKEN_LOG_DEBUG(LABEL, "~SoftBusChannel()");
55 }
56
BuildConnection()57 int SoftBusChannel::BuildConnection()
58 {
59 CancelCloseConnectionIfNeeded();
60 if (session_ != Constant::INVALID_SESSION) {
61 ACCESSTOKEN_LOG_INFO(LABEL, "session is exist, no need open again.");
62 return Constant::SUCCESS;
63 }
64
65 std::unique_lock<std::mutex> lock(sessionMutex_);
66 if (session_ == Constant::INVALID_SESSION) {
67 ACCESSTOKEN_LOG_INFO(LABEL, "open session with device: %{public}s",
68 ConstantCommon::EncryptDevId(deviceId_).c_str());
69 int session = SoftBusManager::GetInstance().OpenSession(deviceId_);
70 if (session == Constant::INVALID_SESSION) {
71 ACCESSTOKEN_LOG_ERROR(LABEL, "open session failed.");
72 return Constant::FAILURE;
73 }
74 session_ = session;
75 }
76 return Constant::SUCCESS;
77 }
78
CloseConnection()79 void SoftBusChannel::CloseConnection()
80 {
81 ACCESSTOKEN_LOG_DEBUG(LABEL, "close connection");
82 std::unique_lock<std::mutex> lock(mutex_);
83 if (isDelayClosing_) {
84 return;
85 }
86
87 std::shared_ptr<TokenSyncEventHandler> handler =
88 DelayedSingleton<TokenSyncManagerService>::GetInstance()->GetSendEventHandler();
89 if (handler == nullptr) {
90 ACCESSTOKEN_LOG_ERROR(LABEL, "fail to get EventHandler");
91 return;
92 }
93 auto thisPtr = shared_from_this();
94 std::function<void()> delayed = ([thisPtr]() {
95 std::unique_lock<std::mutex> lock(thisPtr->sessionMutex_);
96 if (thisPtr->isSessionUsing_) {
97 ACCESSTOKEN_LOG_DEBUG(LABEL, "session is in using, cancel close session");
98 } else {
99 SoftBusManager::GetInstance().CloseSession(thisPtr->session_);
100 thisPtr->session_ = Constant::INVALID_SESSION;
101 ACCESSTOKEN_LOG_INFO(LABEL, "close session for device: %{public}s",
102 ConstantCommon::EncryptDevId(thisPtr->deviceId_).c_str());
103 }
104 thisPtr->isDelayClosing_ = false;
105 });
106
107 ACCESSTOKEN_LOG_DEBUG(LABEL, "close session after %{public}d ms", WAIT_SESSION_CLOSE_MILLISECONDS);
108 handler->ProxyPostTask(delayed, TASK_NAME_CLOSE_SESSION, WAIT_SESSION_CLOSE_MILLISECONDS);
109
110 isDelayClosing_ = true;
111 }
112
Release()113 void SoftBusChannel::Release()
114 {
115 std::shared_ptr<TokenSyncEventHandler> handler =
116 DelayedSingleton<TokenSyncManagerService>::GetInstance()->GetSendEventHandler();
117 if (handler == nullptr) {
118 ACCESSTOKEN_LOG_ERROR(LABEL, "fail to get EventHandler");
119 return;
120 }
121 handler->ProxyRemoveTask(TASK_NAME_CLOSE_SESSION);
122 }
123
ExecuteCommand(const std::string & commandName,const std::string & jsonPayload)124 std::string SoftBusChannel::ExecuteCommand(const std::string &commandName, const std::string &jsonPayload)
125 {
126 if (commandName.empty() || jsonPayload.empty()) {
127 ACCESSTOKEN_LOG_ERROR(LABEL, "invalid params, commandName: %{public}s, jsonPayload: %{public}s",
128 commandName.c_str(), jsonPayload.c_str());
129 return "";
130 }
131
132 // to use a lib like libuuid
133 int uuidStrLen = 37; // 32+4+1
134 char uuidbuf[uuidStrLen];
135 random_uuid(uuidbuf, uuidStrLen);
136 std::string uuid(uuidbuf);
137 ACCESSTOKEN_LOG_DEBUG(LABEL, "generated message uuid: %{public}s", uuid.c_str());
138
139 int len = static_cast<int32_t>(RPC_TRANSFER_HEAD_BYTES_LENGTH + jsonPayload.length());
140 unsigned char *buf = new (std::nothrow) unsigned char[len + 1];
141 if (buf == nullptr) {
142 ACCESSTOKEN_LOG_ERROR(LABEL, "no enough memory: %{public}d", len);
143 return "";
144 }
145 (void)memset_s(buf, len + 1, 0, len + 1);
146 int result = PrepareBytes(REQUEST_TYPE, uuid, commandName, jsonPayload, buf, len);
147 if (result != Constant::SUCCESS) {
148 delete[] buf;
149 return "";
150 }
151
152 std::unique_lock<std::mutex> lock(sessionMutex_);
153 std::function<void(const std::string &)> callback = [&](const std::string &result) {
154 ACCESSTOKEN_LOG_INFO(LABEL, "onResponse called, data: %{public}s", result.c_str());
155 responseResult_ = std::string(result);
156 loadedCond_.notify_all();
157 ACCESSTOKEN_LOG_DEBUG(LABEL, "onResponse called end");
158 };
159 callbacks_.insert(std::pair<std::string, std::function<void(std::string)>>(uuid, callback));
160
161 isSessionUsing_ = true;
162 lock.unlock();
163
164 int retCode = SendRequestBytes(buf, len);
165 delete[] buf;
166
167 std::unique_lock<std::mutex> lock2(sessionMutex_);
168 if (retCode != Constant::SUCCESS) {
169 ACCESSTOKEN_LOG_ERROR(LABEL, "send request data failed: %{public}d ", retCode);
170 callbacks_.erase(uuid);
171 isSessionUsing_ = false;
172 return "";
173 }
174
175 ACCESSTOKEN_LOG_DEBUG(LABEL, "wait command response");
176 if (loadedCond_.wait_for(lock2, std::chrono::milliseconds(EXECUTE_COMMAND_TIME_OUT)) == std::cv_status::timeout) {
177 ACCESSTOKEN_LOG_WARN(LABEL, "time out to wait response.");
178 callbacks_.erase(uuid);
179 isSessionUsing_ = false;
180 return "";
181 }
182
183 isSessionUsing_ = false;
184 return responseResult_;
185 }
186
HandleDataReceived(int session,const unsigned char * bytes,int length)187 void SoftBusChannel::HandleDataReceived(int session, const unsigned char *bytes, int length)
188 {
189 ACCESSTOKEN_LOG_DEBUG(LABEL, "HandleDataReceived");
190 #ifdef DEBUG_API_PERFORMANCE
191 ACCESSTOKEN_LOG_INFO(LABEL, "api_performance:recieve message from softbus");
192 #endif
193 if (session <= 0 || length <= 0) {
194 ACCESSTOKEN_LOG_ERROR(LABEL, "invalid params: session: %{public}d, data length: %{public}d", session, length);
195 return;
196 }
197 std::string receiveData = Decompress(bytes, length);
198 if (receiveData.empty()) {
199 ACCESSTOKEN_LOG_ERROR(LABEL, "invalid parameter bytes");
200 return;
201 }
202 std::shared_ptr<SoftBusMessage> message = SoftBusMessage::FromJson(receiveData);
203 if (message == nullptr) {
204 ACCESSTOKEN_LOG_DEBUG(LABEL, "invalid json string");
205 return;
206 }
207 if (!message->IsValid()) {
208 ACCESSTOKEN_LOG_DEBUG(LABEL, "invalid data, has empty field");
209 return;
210 }
211
212 std::string type = message->GetType();
213 if (REQUEST_TYPE == (type)) {
214 std::function<void()> delayed = ([=]() {
215 HandleRequest(session, message->GetId(), message->GetCommandName(), message->GetJsonPayload());
216 });
217
218 std::shared_ptr<TokenSyncEventHandler> handler =
219 DelayedSingleton<TokenSyncManagerService>::GetInstance()->GetRecvEventHandler();
220 if (handler == nullptr) {
221 ACCESSTOKEN_LOG_ERROR(LABEL, "fail to get EventHandler");
222 return;
223 }
224 handler->ProxyPostTask(delayed, "HandleDataReceived_HandleRequest");
225 } else if (RESPONSE_TYPE == (type)) {
226 HandleResponse(message->GetId(), message->GetJsonPayload());
227 } else {
228 ACCESSTOKEN_LOG_ERROR(LABEL, "invalid type: %{public}s ", type.c_str());
229 }
230 }
231
PrepareBytes(const std::string & type,const std::string & id,const std::string & commandName,const std::string & jsonPayload,const unsigned char * bytes,int & bytesLength)232 int SoftBusChannel::PrepareBytes(const std::string &type, const std::string &id, const std::string &commandName,
233 const std::string &jsonPayload, const unsigned char *bytes, int &bytesLength)
234 {
235 SoftBusMessage messageEntity(type, id, commandName, jsonPayload);
236 std::string json = messageEntity.ToJson();
237 return Compress(json, bytes, bytesLength);
238 }
239
Compress(const std::string & json,const unsigned char * compressedBytes,int & compressedLength)240 int SoftBusChannel::Compress(const std::string &json, const unsigned char *compressedBytes, int &compressedLength)
241 {
242 uLong len = compressBound(json.size());
243 // length will not so that long
244 if (compressedLength > 0 && static_cast<int32_t>(len) > compressedLength) {
245 ACCESSTOKEN_LOG_ERROR(LABEL,
246 "compress error. data length overflow, bound length: %{public}d, buffer length: %{public}d",
247 static_cast<int32_t>(len), compressedLength);
248 return Constant::FAILURE;
249 }
250
251 int result = compress(const_cast<Byte *>(compressedBytes), &len,
252 reinterpret_cast<unsigned char *>(const_cast<char *>(json.c_str())), json.size() + 1);
253 if (result != Z_OK) {
254 ACCESSTOKEN_LOG_ERROR(LABEL, "compress failed! error code: %{public}d", result);
255 return result;
256 }
257 ACCESSTOKEN_LOG_DEBUG(LABEL, "compress complete. compress %{public}d bytes to %{public}d", compressedLength,
258 static_cast<int32_t>(len));
259 compressedLength = static_cast<int32_t>(len);
260 return Constant::SUCCESS;
261 }
262
Decompress(const unsigned char * bytes,const int length)263 std::string SoftBusChannel::Decompress(const unsigned char *bytes, const int length)
264 {
265 ACCESSTOKEN_LOG_DEBUG(LABEL, "input length: %{public}d", length);
266 uLong len = RPC_TRANSFER_BYTES_MAX_LENGTH;
267 unsigned char *buf = new (std::nothrow) unsigned char[len + 1];
268 if (buf == nullptr) {
269 ACCESSTOKEN_LOG_ERROR(LABEL, "no enough memory!");
270 return "";
271 }
272 (void)memset_s(buf, len + 1, 0, len + 1);
273 int result = uncompress(buf, &len, const_cast<unsigned char *>(bytes), length);
274 if (result != Z_OK) {
275 ACCESSTOKEN_LOG_ERROR(LABEL,
276 "uncompress failed, error code: %{public}d, bound length: %{public}d, buffer length: %{public}d", result,
277 static_cast<int32_t>(len), length);
278 delete[] buf;
279 return "";
280 }
281 buf[len] = '\0';
282 std::string str(reinterpret_cast<char *>(buf));
283 delete[] buf;
284 return str;
285 }
286
SendRequestBytes(const unsigned char * bytes,const int bytesLength)287 int SoftBusChannel::SendRequestBytes(const unsigned char *bytes, const int bytesLength)
288 {
289 if (bytesLength == 0) {
290 ACCESSTOKEN_LOG_ERROR(LABEL, "bytes data is invalid.");
291 return Constant::FAILURE;
292 }
293
294 std::unique_lock<std::mutex> lock(sessionMutex_);
295 if (CheckSessionMayReopenLocked() != Constant::SUCCESS) {
296 ACCESSTOKEN_LOG_ERROR(LABEL, "session invalid and reopen failed!");
297 return Constant::FAILURE;
298 }
299
300 ACCESSTOKEN_LOG_DEBUG(LABEL, "send len (after compress len)= %{public}d", bytesLength);
301 #ifdef DEBUG_API_PERFORMANCE
302 ACCESSTOKEN_LOG_INFO(LABEL, "api_performance:send command to softbus");
303 #endif
304 int result = ::SendBytes(session_, bytes, bytesLength);
305 if (result != Constant::SUCCESS) {
306 ACCESSTOKEN_LOG_ERROR(LABEL, "fail to send! result= %{public}d", result);
307 return Constant::FAILURE;
308 }
309 ACCESSTOKEN_LOG_DEBUG(LABEL, "send successfully.");
310 return Constant::SUCCESS;
311 }
312
CheckSessionMayReopenLocked()313 int SoftBusChannel::CheckSessionMayReopenLocked()
314 {
315 // when session is opened, we got a valid sessionid, when session closed, we will reset sessionid.
316 if (IsSessionAvailable()) {
317 return Constant::SUCCESS;
318 }
319 int session = SoftBusManager::GetInstance().OpenSession(deviceId_);
320 if (session != Constant::INVALID_SESSION) {
321 session_ = session;
322 return Constant::SUCCESS;
323 }
324 return Constant::FAILURE;
325 }
326
IsSessionAvailable()327 bool SoftBusChannel::IsSessionAvailable()
328 {
329 return session_ > Constant::INVALID_SESSION;
330 }
331
CancelCloseConnectionIfNeeded()332 void SoftBusChannel::CancelCloseConnectionIfNeeded()
333 {
334 std::unique_lock<std::mutex> lock(mutex_);
335 if (!isDelayClosing_) {
336 return;
337 }
338 ACCESSTOKEN_LOG_DEBUG(LABEL, "cancel close connection");
339
340 Release();
341 isDelayClosing_ = false;
342 }
343
HandleRequest(int session,const std::string & id,const std::string & commandName,const std::string & jsonPayload)344 void SoftBusChannel::HandleRequest(int session, const std::string &id, const std::string &commandName,
345 const std::string &jsonPayload)
346 {
347 std::shared_ptr<BaseRemoteCommand> command =
348 RemoteCommandFactory::GetInstance().NewRemoteCommandFromJson(commandName, jsonPayload);
349 if (command == nullptr) {
350 // send result back directly
351 ACCESSTOKEN_LOG_WARN(LABEL, "command %{public}s cannot get from json %{public}s", commandName.c_str(),
352 jsonPayload.c_str());
353
354 int sendlen = static_cast<int32_t>(RPC_TRANSFER_HEAD_BYTES_LENGTH + jsonPayload.length());
355 unsigned char *sendbuf = new (std::nothrow) unsigned char[sendlen + 1];
356 if (sendbuf == nullptr) {
357 ACCESSTOKEN_LOG_ERROR(LABEL, "no enough memory: %{public}d", sendlen);
358 return;
359 }
360 (void)memset_s(sendbuf, sendlen + 1, 0, sendlen + 1);
361 int sendResult = PrepareBytes(RESPONSE_TYPE, id, commandName, jsonPayload, sendbuf, sendlen);
362 if (sendResult != Constant::SUCCESS) {
363 delete[] sendbuf;
364 return;
365 }
366 int sendResultCode = SendResponseBytes(session, sendbuf, sendlen);
367 delete[] sendbuf;
368 ACCESSTOKEN_LOG_DEBUG(LABEL, "send response result= %{public}d ", sendResultCode);
369 return;
370 }
371
372 // execute command
373 command->Execute();
374 ACCESSTOKEN_LOG_DEBUG(LABEL, "command uniqueId: %{public}s, finish with status: %{public}d, message: %{public}s",
375 command->remoteProtocol_.uniqueId.c_str(), command->remoteProtocol_.statusCode,
376 command->remoteProtocol_.message.c_str());
377
378 // send result back
379 std::string resultJsonPayload = command->ToJsonPayload();
380 int len = static_cast<int32_t>(RPC_TRANSFER_HEAD_BYTES_LENGTH + resultJsonPayload.length());
381 unsigned char *buf = new (std::nothrow) unsigned char[len + 1];
382 if (buf == nullptr) {
383 ACCESSTOKEN_LOG_ERROR(LABEL, "no enough memory: %{public}d", len);
384 return;
385 }
386 (void)memset_s(buf, len + 1, 0, len + 1);
387 int result = PrepareBytes(RESPONSE_TYPE, id, commandName, resultJsonPayload, buf, len);
388 if (result != Constant::SUCCESS) {
389 delete[] buf;
390 return;
391 }
392 int retCode = SendResponseBytes(session, buf, len);
393 delete[] buf;
394 ACCESSTOKEN_LOG_DEBUG(LABEL, "send response result= %{public}d", retCode);
395 }
396
HandleResponse(const std::string & id,const std::string & jsonPayload)397 void SoftBusChannel::HandleResponse(const std::string &id, const std::string &jsonPayload)
398 {
399 std::unique_lock<std::mutex> lock(sessionMutex_);
400 auto callback = callbacks_.find(id);
401 if (callback != callbacks_.end()) {
402 (callback->second)(jsonPayload);
403 callbacks_.erase(callback);
404 }
405 }
406
SendResponseBytes(int session,const unsigned char * bytes,const int bytesLength)407 int SoftBusChannel::SendResponseBytes(int session, const unsigned char *bytes, const int bytesLength)
408 {
409 ACCESSTOKEN_LOG_DEBUG(LABEL, "send len (after compress len)= %{public}d", bytesLength);
410 int result = ::SendBytes(session, bytes, bytesLength);
411 if (result != Constant::SUCCESS) {
412 ACCESSTOKEN_LOG_ERROR(LABEL, "fail to send! result= %{public}d", result);
413 return Constant::FAILURE;
414 }
415 ACCESSTOKEN_LOG_DEBUG(LABEL, "send successfully.");
416 return Constant::SUCCESS;
417 }
418
FromJson(const std::string & jsonString)419 std::shared_ptr<SoftBusMessage> SoftBusMessage::FromJson(const std::string &jsonString)
420 {
421 nlohmann::json json;
422 if (!json.accept(jsonString)) {
423 return nullptr;
424 }
425 json = json.parse(jsonString, nullptr, false);
426 if (json.is_discarded() || (!json.is_object())) {
427 ACCESSTOKEN_LOG_ERROR(LABEL, "failed to parse jsonString");
428 return nullptr;
429 }
430
431 std::string type;
432 std::string id;
433 std::string commandName;
434 std::string jsonPayload;
435 if (json.find("type") != json.end() && json.at("type").is_string()) {
436 json.at("type").get_to(type);
437 }
438 if (json.find("id") != json.end() && json.at("id").is_string()) {
439 json.at("id").get_to(id);
440 }
441 if (json.find("commandName") != json.end() && json.at("commandName").is_string()) {
442 json.at("commandName").get_to(commandName);
443 }
444 if (json.find("jsonPayload") != json.end() && json.at("jsonPayload").is_string()) {
445 json.at("jsonPayload").get_to(jsonPayload);
446 }
447 if (type.empty() || id.empty() || commandName.empty() || jsonPayload.empty()) {
448 ACCESSTOKEN_LOG_ERROR(LABEL, "failed to get json string(json format error)");
449 return nullptr;
450 }
451 std::shared_ptr<SoftBusMessage> message = std::make_shared<SoftBusMessage>(type, id, commandName, jsonPayload);
452 return message;
453 }
454 } // namespace AccessToken
455 } // namespace Security
456 } // namespace OHOS
457