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