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", commandName.c_str());
128 return "";
129 }
130
131 // to use a lib like libuuid
132 int uuidStrLen = 37; // 32+4+1
133 char uuidbuf[uuidStrLen];
134 random_uuid(uuidbuf, uuidStrLen);
135 std::string uuid(uuidbuf);
136 ACCESSTOKEN_LOG_DEBUG(LABEL, "generated message uuid: %{public}s", uuid.c_str());
137
138 int len = static_cast<int32_t>(RPC_TRANSFER_HEAD_BYTES_LENGTH + jsonPayload.length());
139 unsigned char *buf = new (std::nothrow) unsigned char[len + 1];
140 if (buf == nullptr) {
141 ACCESSTOKEN_LOG_ERROR(LABEL, "no enough memory: %{public}d", len);
142 return "";
143 }
144 (void)memset_s(buf, len + 1, 0, len + 1);
145 int result = PrepareBytes(REQUEST_TYPE, uuid, commandName, jsonPayload, buf, len);
146 if (result != Constant::SUCCESS) {
147 delete[] buf;
148 return "";
149 }
150
151 std::unique_lock<std::mutex> lock(sessionMutex_);
152 std::function<void(const std::string &)> callback = [&](const std::string &result) {
153 responseResult_ = std::string(result);
154 loadedCond_.notify_all();
155 ACCESSTOKEN_LOG_DEBUG(LABEL, "onResponse called end");
156 };
157 callbacks_.insert(std::pair<std::string, std::function<void(std::string)>>(uuid, callback));
158
159 isSessionUsing_ = true;
160 lock.unlock();
161
162 int retCode = SendRequestBytes(buf, len);
163 delete[] buf;
164
165 std::unique_lock<std::mutex> lock2(sessionMutex_);
166 if (retCode != Constant::SUCCESS) {
167 ACCESSTOKEN_LOG_ERROR(LABEL, "send request data failed: %{public}d ", retCode);
168 callbacks_.erase(uuid);
169 isSessionUsing_ = false;
170 return "";
171 }
172
173 ACCESSTOKEN_LOG_DEBUG(LABEL, "wait command response");
174 if (loadedCond_.wait_for(lock2, std::chrono::milliseconds(EXECUTE_COMMAND_TIME_OUT)) == std::cv_status::timeout) {
175 ACCESSTOKEN_LOG_WARN(LABEL, "time out to wait response.");
176 callbacks_.erase(uuid);
177 isSessionUsing_ = false;
178 return "";
179 }
180
181 isSessionUsing_ = false;
182 return responseResult_;
183 }
184
HandleDataReceived(int session,const unsigned char * bytes,int length)185 void SoftBusChannel::HandleDataReceived(int session, const unsigned char *bytes, int length)
186 {
187 ACCESSTOKEN_LOG_DEBUG(LABEL, "HandleDataReceived");
188 #ifdef DEBUG_API_PERFORMANCE
189 ACCESSTOKEN_LOG_INFO(LABEL, "api_performance:recieve message from softbus");
190 #endif
191 if (session <= 0 || length <= 0) {
192 ACCESSTOKEN_LOG_ERROR(LABEL, "invalid params: session: %{public}d, data length: %{public}d", session, length);
193 return;
194 }
195 std::string receiveData = Decompress(bytes, length);
196 if (receiveData.empty()) {
197 ACCESSTOKEN_LOG_ERROR(LABEL, "invalid parameter bytes");
198 return;
199 }
200 std::shared_ptr<SoftBusMessage> message = SoftBusMessage::FromJson(receiveData);
201 if (message == nullptr) {
202 ACCESSTOKEN_LOG_DEBUG(LABEL, "invalid json string");
203 return;
204 }
205 if (!message->IsValid()) {
206 ACCESSTOKEN_LOG_DEBUG(LABEL, "invalid data, has empty field");
207 return;
208 }
209
210 std::string type = message->GetType();
211 if (REQUEST_TYPE == (type)) {
212 std::function<void()> delayed = ([=]() {
213 HandleRequest(session, message->GetId(), message->GetCommandName(), message->GetJsonPayload());
214 });
215
216 std::shared_ptr<TokenSyncEventHandler> handler =
217 DelayedSingleton<TokenSyncManagerService>::GetInstance()->GetRecvEventHandler();
218 if (handler == nullptr) {
219 ACCESSTOKEN_LOG_ERROR(LABEL, "fail to get EventHandler");
220 return;
221 }
222 handler->ProxyPostTask(delayed, "HandleDataReceived_HandleRequest");
223 } else if (RESPONSE_TYPE == (type)) {
224 HandleResponse(message->GetId(), message->GetJsonPayload());
225 } else {
226 ACCESSTOKEN_LOG_ERROR(LABEL, "invalid type: %{public}s ", type.c_str());
227 }
228 }
229
PrepareBytes(const std::string & type,const std::string & id,const std::string & commandName,const std::string & jsonPayload,const unsigned char * bytes,int & bytesLength)230 int SoftBusChannel::PrepareBytes(const std::string &type, const std::string &id, const std::string &commandName,
231 const std::string &jsonPayload, const unsigned char *bytes, int &bytesLength)
232 {
233 SoftBusMessage messageEntity(type, id, commandName, jsonPayload);
234 std::string json = messageEntity.ToJson();
235 return Compress(json, bytes, bytesLength);
236 }
237
Compress(const std::string & json,const unsigned char * compressedBytes,int & compressedLength)238 int SoftBusChannel::Compress(const std::string &json, const unsigned char *compressedBytes, int &compressedLength)
239 {
240 uLong len = compressBound(json.size());
241 // length will not so that long
242 if (compressedLength > 0 && static_cast<int32_t>(len) > compressedLength) {
243 ACCESSTOKEN_LOG_ERROR(LABEL,
244 "compress error. data length overflow, bound length: %{public}d, buffer length: %{public}d",
245 static_cast<int32_t>(len), compressedLength);
246 return Constant::FAILURE;
247 }
248
249 int result = compress(const_cast<Byte *>(compressedBytes), &len,
250 reinterpret_cast<unsigned char *>(const_cast<char *>(json.c_str())), json.size() + 1);
251 if (result != Z_OK) {
252 ACCESSTOKEN_LOG_ERROR(LABEL, "compress failed! error code: %{public}d", result);
253 return result;
254 }
255 ACCESSTOKEN_LOG_DEBUG(LABEL, "compress complete. compress %{public}d bytes to %{public}d", compressedLength,
256 static_cast<int32_t>(len));
257 compressedLength = static_cast<int32_t>(len);
258 return Constant::SUCCESS;
259 }
260
Decompress(const unsigned char * bytes,const int length)261 std::string SoftBusChannel::Decompress(const unsigned char *bytes, const int length)
262 {
263 ACCESSTOKEN_LOG_DEBUG(LABEL, "input length: %{public}d", length);
264 uLong len = RPC_TRANSFER_BYTES_MAX_LENGTH;
265 unsigned char *buf = new (std::nothrow) unsigned char[len + 1];
266 if (buf == nullptr) {
267 ACCESSTOKEN_LOG_ERROR(LABEL, "no enough memory!");
268 return "";
269 }
270 (void)memset_s(buf, len + 1, 0, len + 1);
271 int result = uncompress(buf, &len, const_cast<unsigned char *>(bytes), length);
272 if (result != Z_OK) {
273 ACCESSTOKEN_LOG_ERROR(LABEL,
274 "uncompress failed, error code: %{public}d, bound length: %{public}d, buffer length: %{public}d", result,
275 static_cast<int32_t>(len), length);
276 delete[] buf;
277 return "";
278 }
279 buf[len] = '\0';
280 std::string str(reinterpret_cast<char *>(buf));
281 delete[] buf;
282 return str;
283 }
284
SendRequestBytes(const unsigned char * bytes,const int bytesLength)285 int SoftBusChannel::SendRequestBytes(const unsigned char *bytes, const int bytesLength)
286 {
287 if (bytesLength == 0) {
288 ACCESSTOKEN_LOG_ERROR(LABEL, "bytes data is invalid.");
289 return Constant::FAILURE;
290 }
291
292 std::unique_lock<std::mutex> lock(sessionMutex_);
293 if (CheckSessionMayReopenLocked() != Constant::SUCCESS) {
294 ACCESSTOKEN_LOG_ERROR(LABEL, "session invalid and reopen failed!");
295 return Constant::FAILURE;
296 }
297
298 ACCESSTOKEN_LOG_DEBUG(LABEL, "send len (after compress len)= %{public}d", bytesLength);
299 #ifdef DEBUG_API_PERFORMANCE
300 ACCESSTOKEN_LOG_INFO(LABEL, "api_performance:send command to softbus");
301 #endif
302 int result = ::SendBytes(session_, bytes, bytesLength);
303 if (result != Constant::SUCCESS) {
304 ACCESSTOKEN_LOG_ERROR(LABEL, "fail to send! result= %{public}d", result);
305 return Constant::FAILURE;
306 }
307 ACCESSTOKEN_LOG_DEBUG(LABEL, "send successfully.");
308 return Constant::SUCCESS;
309 }
310
CheckSessionMayReopenLocked()311 int SoftBusChannel::CheckSessionMayReopenLocked()
312 {
313 // when session is opened, we got a valid sessionid, when session closed, we will reset sessionid.
314 if (IsSessionAvailable()) {
315 return Constant::SUCCESS;
316 }
317 int session = SoftBusManager::GetInstance().OpenSession(deviceId_);
318 if (session != Constant::INVALID_SESSION) {
319 session_ = session;
320 return Constant::SUCCESS;
321 }
322 return Constant::FAILURE;
323 }
324
IsSessionAvailable()325 bool SoftBusChannel::IsSessionAvailable()
326 {
327 return session_ > Constant::INVALID_SESSION;
328 }
329
CancelCloseConnectionIfNeeded()330 void SoftBusChannel::CancelCloseConnectionIfNeeded()
331 {
332 std::unique_lock<std::mutex> lock(mutex_);
333 if (!isDelayClosing_) {
334 return;
335 }
336 ACCESSTOKEN_LOG_DEBUG(LABEL, "cancel close connection");
337
338 Release();
339 isDelayClosing_ = false;
340 }
341
HandleRequest(int session,const std::string & id,const std::string & commandName,const std::string & jsonPayload)342 void SoftBusChannel::HandleRequest(int session, const std::string &id, const std::string &commandName,
343 const std::string &jsonPayload)
344 {
345 std::shared_ptr<BaseRemoteCommand> command =
346 RemoteCommandFactory::GetInstance().NewRemoteCommandFromJson(commandName, jsonPayload);
347 if (command == nullptr) {
348 // send result back directly
349 ACCESSTOKEN_LOG_WARN(LABEL, "command %{public}s cannot get from json", commandName.c_str());
350
351 int sendlen = static_cast<int32_t>(RPC_TRANSFER_HEAD_BYTES_LENGTH + jsonPayload.length());
352 unsigned char *sendbuf = new (std::nothrow) unsigned char[sendlen + 1];
353 if (sendbuf == nullptr) {
354 ACCESSTOKEN_LOG_ERROR(LABEL, "no enough memory: %{public}d", sendlen);
355 return;
356 }
357 (void)memset_s(sendbuf, sendlen + 1, 0, sendlen + 1);
358 int sendResult = PrepareBytes(RESPONSE_TYPE, id, commandName, jsonPayload, sendbuf, sendlen);
359 if (sendResult != Constant::SUCCESS) {
360 delete[] sendbuf;
361 return;
362 }
363 int sendResultCode = SendResponseBytes(session, sendbuf, sendlen);
364 delete[] sendbuf;
365 ACCESSTOKEN_LOG_DEBUG(LABEL, "send response result= %{public}d ", sendResultCode);
366 return;
367 }
368
369 // execute command
370 command->Execute();
371 ACCESSTOKEN_LOG_DEBUG(LABEL, "command uniqueId: %{public}s, finish with status: %{public}d, message: %{public}s",
372 command->remoteProtocol_.uniqueId.c_str(), command->remoteProtocol_.statusCode,
373 command->remoteProtocol_.message.c_str());
374
375 // send result back
376 std::string resultJsonPayload = command->ToJsonPayload();
377 int len = static_cast<int32_t>(RPC_TRANSFER_HEAD_BYTES_LENGTH + resultJsonPayload.length());
378 unsigned char *buf = new (std::nothrow) unsigned char[len + 1];
379 if (buf == nullptr) {
380 ACCESSTOKEN_LOG_ERROR(LABEL, "no enough memory: %{public}d", len);
381 return;
382 }
383 (void)memset_s(buf, len + 1, 0, len + 1);
384 int result = PrepareBytes(RESPONSE_TYPE, id, commandName, resultJsonPayload, buf, len);
385 if (result != Constant::SUCCESS) {
386 delete[] buf;
387 return;
388 }
389 int retCode = SendResponseBytes(session, buf, len);
390 delete[] buf;
391 ACCESSTOKEN_LOG_DEBUG(LABEL, "send response result= %{public}d", retCode);
392 }
393
HandleResponse(const std::string & id,const std::string & jsonPayload)394 void SoftBusChannel::HandleResponse(const std::string &id, const std::string &jsonPayload)
395 {
396 std::unique_lock<std::mutex> lock(sessionMutex_);
397 auto callback = callbacks_.find(id);
398 if (callback != callbacks_.end()) {
399 (callback->second)(jsonPayload);
400 callbacks_.erase(callback);
401 }
402 }
403
SendResponseBytes(int session,const unsigned char * bytes,const int bytesLength)404 int SoftBusChannel::SendResponseBytes(int session, const unsigned char *bytes, const int bytesLength)
405 {
406 ACCESSTOKEN_LOG_DEBUG(LABEL, "send len (after compress len)= %{public}d", bytesLength);
407 int result = ::SendBytes(session, bytes, bytesLength);
408 if (result != Constant::SUCCESS) {
409 ACCESSTOKEN_LOG_ERROR(LABEL, "fail to send! result= %{public}d", result);
410 return Constant::FAILURE;
411 }
412 ACCESSTOKEN_LOG_DEBUG(LABEL, "send successfully.");
413 return Constant::SUCCESS;
414 }
415
FromJson(const std::string & jsonString)416 std::shared_ptr<SoftBusMessage> SoftBusMessage::FromJson(const std::string &jsonString)
417 {
418 nlohmann::json json;
419 if (!json.accept(jsonString)) {
420 return nullptr;
421 }
422 json = json.parse(jsonString, nullptr, false);
423 if (json.is_discarded() || (!json.is_object())) {
424 ACCESSTOKEN_LOG_ERROR(LABEL, "failed to parse jsonString");
425 return nullptr;
426 }
427
428 std::string type;
429 std::string id;
430 std::string commandName;
431 std::string jsonPayload;
432 if (json.find("type") != json.end() && json.at("type").is_string()) {
433 json.at("type").get_to(type);
434 }
435 if (json.find("id") != json.end() && json.at("id").is_string()) {
436 json.at("id").get_to(id);
437 }
438 if (json.find("commandName") != json.end() && json.at("commandName").is_string()) {
439 json.at("commandName").get_to(commandName);
440 }
441 if (json.find("jsonPayload") != json.end() && json.at("jsonPayload").is_string()) {
442 json.at("jsonPayload").get_to(jsonPayload);
443 }
444 if (type.empty() || id.empty() || commandName.empty() || jsonPayload.empty()) {
445 ACCESSTOKEN_LOG_ERROR(LABEL, "failed to get json string(json format error)");
446 return nullptr;
447 }
448 std::shared_ptr<SoftBusMessage> message = std::make_shared<SoftBusMessage>(type, id, commandName, jsonPayload);
449 return message;
450 }
451 } // namespace AccessToken
452 } // namespace Security
453 } // namespace OHOS
454