1 /*
2 * Copyright (C) 2023 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 #ifndef LOG_TAG
16 #define LOG_TAG "bt_napi_socket_spp_client"
17 #endif
18
19 #include "bluetooth_errorcode.h"
20 #include "bluetooth_host.h"
21 #include "datetime_ex.h"
22 #include "napi_bluetooth_spp_client.h"
23 #include "napi_bluetooth_error.h"
24 #include "napi_bluetooth_utils.h"
25 #include "napi_async_work.h"
26 #include "napi_native_object.h"
27 #include "securec.h"
28 #include <limits>
29 #include <unistd.h>
30 #include <uv.h>
31 #include "../parser/napi_parser_utils.h"
32
33 namespace OHOS {
34 namespace Bluetooth {
35 std::map<int, std::shared_ptr<NapiSppClient>> NapiSppClient::clientMap;
36 int NapiSppClient::count = 0;
37 const int SOCKET_BUFFER_SIZE = 1024;
38
CheckSppConnectParams(napi_env env,napi_callback_info info,std::string & deviceId,SppConnectCallbackInfo * callbackInfo)39 static napi_status CheckSppConnectParams(
40 napi_env env, napi_callback_info info, std::string &deviceId, SppConnectCallbackInfo *callbackInfo)
41 {
42 HILOGI("enter");
43 size_t argc = ARGS_SIZE_THREE;
44 napi_value argv[ARGS_SIZE_THREE] = {0};
45
46 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
47 NAPI_BT_RETURN_IF((argc != ARGS_SIZE_THREE && argc != ARGS_SIZE_THREE - CALLBACK_SIZE),
48 "Requires 2 or 3 arguments.", napi_invalid_arg);
49 NAPI_BT_RETURN_IF(!ParseString(env, deviceId, argv[PARAM0]),
50 "Wrong argument type. String expected.", napi_invalid_arg);
51
52 callbackInfo->env_ = env;
53 callbackInfo->sppOption_ = GetSppOptionFromJS(env, argv[PARAM1]);
54 NAPI_BT_RETURN_IF((callbackInfo->sppOption_ == nullptr), "GetSppOptionFromJS faild.", napi_invalid_arg);
55 callbackInfo->deviceId_ = deviceId;
56
57 napi_value promise = nullptr;
58
59 if (argc == ARGS_SIZE_THREE) {
60 // Callback mode
61 HILOGI("callback mode");
62 napi_valuetype valueType = napi_undefined;
63 napi_typeof(env, argv[PARAM2], &valueType);
64 if (valueType != napi_function) {
65 HILOGE("Wrong argument type. Function expected.");
66 delete callbackInfo;
67 callbackInfo = nullptr;
68 return napi_invalid_arg;
69 }
70 napi_create_reference(env, argv[PARAM2], 1, &callbackInfo->callback_);
71 napi_get_undefined(env, &promise);
72 } else {
73 // Promise mode
74 HILOGI("promise mode");
75 napi_create_promise(env, &callbackInfo->deferred_, &promise);
76 }
77 return napi_ok;
78 }
79
GetSppOptionFromJS(napi_env env,napi_value object)80 std::shared_ptr<SppOption> GetSppOptionFromJS(napi_env env, napi_value object)
81 {
82 std::shared_ptr<SppOption> sppOption = std::make_shared<SppOption>();
83 napi_value propertyNameValue = nullptr;
84 napi_value value = nullptr;
85
86 napi_create_string_utf8(env, "uuid", NAPI_AUTO_LENGTH, &propertyNameValue);
87 napi_get_property(env, object, propertyNameValue, &value);
88 bool isSuccess = ParseString(env, sppOption->uuid_, value);
89 if (!isSuccess || (!IsValidUuid(sppOption->uuid_))) {
90 HILOGE("Parse UUID faild.");
91 return nullptr;
92 }
93 HILOGI("uuid is %{public}s", sppOption->uuid_.c_str());
94
95 napi_create_string_utf8(env, "secure", NAPI_AUTO_LENGTH, &propertyNameValue);
96 napi_get_property(env, object, propertyNameValue, &value);
97 ParseBool(env, sppOption->secure_, value);
98 HILOGI("secure is %{public}d", sppOption->secure_);
99
100 int type = 0;
101 napi_create_string_utf8(env, "type", NAPI_AUTO_LENGTH, &propertyNameValue);
102 napi_get_property(env, object, propertyNameValue, &value);
103 ParseInt32(env, type, value);
104 sppOption->type_ = BtSocketType(type);
105 HILOGI("uuid: %{public}s, secure: %{public}d, type: %{public}d",
106 sppOption->uuid_.c_str(), sppOption->secure_, sppOption->type_);
107 return sppOption;
108 }
109
SppConnect(napi_env env,napi_callback_info info)110 napi_value NapiSppClient::SppConnect(napi_env env, napi_callback_info info)
111 {
112 HILOGI("enter");
113 std::string deviceId;
114 SppConnectCallbackInfo *callbackInfo = new SppConnectCallbackInfo();
115 auto status = CheckSppConnectParams(env, info, deviceId, callbackInfo);
116 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
117
118 napi_value resource = nullptr;
119 napi_create_string_utf8(env, "SppConnect", NAPI_AUTO_LENGTH, &resource);
120
121 napi_create_async_work(
122 env, nullptr, resource,
123 [](napi_env env, void* data) {
124 HILOGI("SppConnect execute");
125 SppConnectCallbackInfo* callbackInfo = static_cast<SppConnectCallbackInfo*>(data);
126 callbackInfo->device_ = std::make_shared<BluetoothRemoteDevice>(callbackInfo->deviceId_, 0);
127 callbackInfo->client_ = std::make_shared<ClientSocket>(*callbackInfo->device_,
128 UUID::FromString(callbackInfo->sppOption_->uuid_),
129 callbackInfo->sppOption_->type_, callbackInfo->sppOption_->secure_);
130 HILOGI("SppConnect client_ constructed");
131 callbackInfo->errorCode_ = callbackInfo->client_->Connect(SPP_SOCKET_PSM_VALUE);
132 if (callbackInfo->errorCode_ == BtStatus::BT_SUCCESS) {
133 HILOGI("SppConnect successfully");
134 callbackInfo->errorCode_ = CODE_SUCCESS;
135 } else {
136 HILOGE("SppConnect failed");
137 }
138 },
139 [](napi_env env, napi_status status, void* data) {
140 HILOGI("SppConnect execute back");
141 SppConnectCallbackInfo* callbackInfo = static_cast<SppConnectCallbackInfo*>(data);
142 napi_value result[ARGS_SIZE_TWO] = {0};
143 napi_value callback = 0;
144 napi_value undefined = 0;
145 napi_value callResult = 0;
146 napi_get_undefined(env, &undefined);
147
148 if (callbackInfo->errorCode_ == CODE_SUCCESS) {
149 HILOGI("SppConnect execute back success");
150 std::shared_ptr<NapiSppClient> client = std::make_shared<NapiSppClient>();
151 client->device_ = callbackInfo->device_;
152 client->id_ = NapiSppClient::count++;
153 napi_create_int32(env, client->id_, &result[PARAM1]);
154 client->client_ = callbackInfo->client_;
155 clientMap.insert(std::make_pair(client->id_, client));
156 HILOGI("SppConnect execute back successfully");
157 } else {
158 napi_get_undefined(env, &result[PARAM1]);
159 HILOGI("SppConnect execute back failed");
160 }
161
162 if (callbackInfo->callback_) {
163 // Callback mode
164 HILOGI("SppConnect execute Callback mode");
165 result[PARAM0] = GetCallbackErrorValue(callbackInfo->env_, callbackInfo->errorCode_);
166 napi_get_reference_value(env, callbackInfo->callback_, &callback);
167 napi_call_function(env, undefined, callback, ARGS_SIZE_TWO, result, &callResult);
168 napi_delete_reference(env, callbackInfo->callback_);
169 } else {
170 if (callbackInfo->errorCode_ == CODE_SUCCESS) {
171 // Promise mode
172 HILOGI("SppConnect execute Promise mode successfully");
173 napi_resolve_deferred(env, callbackInfo->deferred_, result[PARAM1]);
174 } else {
175 HILOGI("SppConnect execute Promise mode failed");
176 napi_reject_deferred(env, callbackInfo->deferred_, result[PARAM1]);
177 }
178 }
179 napi_delete_async_work(env, callbackInfo->asyncWork_);
180 delete callbackInfo;
181 callbackInfo = nullptr;
182 },
183 static_cast<void*>(callbackInfo), &callbackInfo->asyncWork_);
184 if (napi_queue_async_work(env, callbackInfo->asyncWork_) != napi_ok) {
185 HILOGE("SppConnect napi_queue_async_work failed");
186 delete callbackInfo;
187 callbackInfo = nullptr;
188 }
189 return NapiGetUndefinedRet(env);
190 }
191
CheckSppCloseClientSocketParams(napi_env env,napi_callback_info info,int & id)192 static napi_status CheckSppCloseClientSocketParams(napi_env env, napi_callback_info info, int &id)
193 {
194 HILOGI("enter");
195 size_t argc = ARGS_SIZE_ONE;
196 napi_value argv[ARGS_SIZE_ONE] = {0};
197 napi_value thisVar = nullptr;
198
199 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
200 NAPI_BT_RETURN_IF((argc != ARGS_SIZE_ONE), "Requires 1 arguments.", napi_invalid_arg);
201 NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM0]), "Wrong argument type. int expected.", napi_invalid_arg);
202 return napi_ok;
203 }
204
SppCloseClientSocket(napi_env env,napi_callback_info info)205 napi_value NapiSppClient::SppCloseClientSocket(napi_env env, napi_callback_info info)
206 {
207 HILOGI("enter");
208 std::shared_ptr<NapiSppClient> client = nullptr;
209 int id = -1;
210 bool isOK = false;
211 auto status = CheckSppCloseClientSocketParams(env, info, id);
212 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
213
214 if (clientMap[id]) {
215 client = clientMap[id];
216 } else {
217 HILOGE("no such key in map.");
218 return NapiGetUndefinedRet(env);
219 }
220
221 if (client->client_) {
222 client->client_->Close();
223 isOK = true;
224 }
225 NAPI_BT_RETURN_IF(napi_release_threadsafe_function(client->sppReadThreadSafeFunc_, napi_tsfn_abort),
226 "inner error", nullptr);
227 clientMap.erase(id);
228 return NapiGetBooleanRet(env, isOK);
229 }
230
CheckSppWriteParams(napi_env env,napi_callback_info info,int & id,uint8_t ** totalBuf,size_t & totalSize)231 static napi_status CheckSppWriteParams(
232 napi_env env, napi_callback_info info, int &id, uint8_t** totalBuf, size_t &totalSize)
233 {
234 HILOGI("enter");
235 size_t argc = ARGS_SIZE_TWO;
236 napi_value argv[ARGS_SIZE_TWO] = {0};
237 napi_value thisVar = nullptr;
238
239 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
240 NAPI_BT_RETURN_IF((argc != ARGS_SIZE_TWO), "Requires 2 arguments.", napi_invalid_arg);
241 NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM0]), "Wrong argument type. int expected.", napi_invalid_arg);
242 NAPI_BT_RETURN_IF(!ParseArrayBuffer(env, totalBuf, totalSize, argv[PARAM1]),
243 "ParseArrayBuffer failed.", napi_invalid_arg);
244 return napi_ok;
245 }
246
SppWrite(napi_env env,napi_callback_info info)247 napi_value NapiSppClient::SppWrite(napi_env env, napi_callback_info info)
248 {
249 HILOGI("enter");
250 uint8_t* totalBuf = nullptr;
251 size_t totalSize = 0;
252 bool isOK = false;
253 int id = -1;
254
255 BluetoothHost *host = &BluetoothHost::GetDefaultHost();
256 auto prohibitedTime = host->GetRefusePolicyProhibitedTime();
257 if (prohibitedTime < 0 || prohibitedTime > GetSecondsSince1970ToNow()) {
258 HILOGE("socket refuse because of Refuse Policy");
259 NAPI_BT_ASSERT_RETURN_FALSE(env, false, BT_ERR_INVALID_PARAM);
260 }
261
262 auto status = CheckSppWriteParams(env, info, id, &totalBuf, totalSize);
263 NAPI_BT_ASSERT_RETURN_FALSE(env, status == napi_ok, BT_ERR_INVALID_PARAM);
264 NAPI_BT_ASSERT_RETURN_FALSE(env, clientMap[id] > 0, BT_ERR_INTERNAL_ERROR);
265 std::shared_ptr<OutputStream> outputStream = clientMap[id]->client_->GetOutputStream();
266 while (totalSize) {
267 int result = outputStream->Write(totalBuf, totalSize);
268 NAPI_BT_ASSERT_RETURN_FALSE(env, result > 0, BT_ERR_SPP_IO);
269 totalSize = totalSize - static_cast<size_t>(result);
270 totalBuf += static_cast<size_t>(result);
271 isOK = true;
272 }
273 return NapiGetBooleanRet(env, isOK);
274 }
275
NapiThreadSafeFuncCallJs(napi_env,napi_value jsCallback,void * context,void * data)276 static void NapiThreadSafeFuncCallJs(napi_env, napi_value jsCallback, void *context, void *data)
277 {
278 if (jsCallback == nullptr) {
279 HILOGE("delete safeFunc, jsCallback is nullptr");
280 return;
281 }
282 BufferCallbackInfo *callbackInfo = static_cast<BufferCallbackInfo *>(data);
283 std::shared_ptr<SppCallbackBuffer> buffer = callbackInfo->PopData();
284 if (buffer == nullptr) {
285 HILOGE("callbackInfo->PopData return nullptr");
286 return;
287 }
288 if (buffer->len_ < 0 || buffer->len_ > SOCKET_BUFFER_SIZE) {
289 HILOGE("buffer->len_ invalid");
290 return;
291 }
292
293 napi_value result = nullptr;
294 uint8_t *bufferData = nullptr;
295 napi_create_arraybuffer(callbackInfo->env_, buffer->len_, (void **)&bufferData, &result);
296 if (memcpy_s(bufferData, buffer->len_, buffer->data_, buffer->len_) != EOK) {
297 HILOGE("memcpy_s failed!");
298 return;
299 }
300
301 napi_value undefined = nullptr;
302 napi_value callResult = nullptr;
303 napi_get_undefined(callbackInfo->env_, &undefined);
304 napi_call_function(callbackInfo->env_, undefined, jsCallback, ARGS_SIZE_ONE, &result, &callResult);
305 }
306
NapiSppCreateThreadSafeFunc(const std::shared_ptr<NapiSppClient> & client)307 static napi_status NapiSppCreateThreadSafeFunc(const std::shared_ptr<NapiSppClient> &client)
308 {
309 napi_value name;
310 napi_threadsafe_function tsfn;
311 const size_t maxQueueSize = 0; // 0 means no limited
312 const size_t initialThreadCount = 1;
313 napi_value callback = nullptr;
314 auto callbackInfo = client->callbackInfos_[REGISTER_SPP_READ_TYPE];
315 NAPI_BT_CALL_RETURN(napi_create_string_utf8(callbackInfo->env_, "SppRead", NAPI_AUTO_LENGTH, &name));
316 NAPI_BT_CALL_RETURN(napi_get_reference_value(callbackInfo->env_, callbackInfo->callback_, &callback));
317 NAPI_BT_CALL_RETURN(napi_create_threadsafe_function(callbackInfo->env_, callback, nullptr,
318 name, maxQueueSize, initialThreadCount, nullptr, nullptr, nullptr, NapiThreadSafeFuncCallJs, &tsfn));
319 if (client->sppReadThreadSafeFunc_ != nullptr) {
320 NAPI_BT_RETURN_IF(napi_release_threadsafe_function(client->sppReadThreadSafeFunc_, napi_tsfn_abort),
321 "inner error", napi_invalid_arg);
322 }
323 client->sppReadThreadSafeFunc_ = tsfn;
324 return napi_ok;
325 }
326
CheckSppClientOn(napi_env env,napi_callback_info info)327 napi_status CheckSppClientOn(napi_env env, napi_callback_info info)
328 {
329 HILOGI("enter");
330 size_t argc = ARGS_SIZE_THREE;
331 napi_value argv[ARGS_SIZE_THREE] = {0};
332 napi_value thisVar = nullptr;
333 int id = -1;
334 std::string type;
335
336 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
337 NAPI_BT_RETURN_IF((argc != ARGS_SIZE_THREE), "Requires 3 arguments.", napi_invalid_arg);
338 NAPI_BT_RETURN_IF(!ParseString(env, type, argv[PARAM0]),
339 "Wrong argument type. String expected.", napi_invalid_arg);
340 NAPI_BT_RETURN_IF(type != REGISTER_SPP_READ_TYPE, "Invalid type.", napi_invalid_arg);
341
342 std::shared_ptr<BluetoothCallbackInfo> callbackInfo = std::make_shared<BufferCallbackInfo>();
343 callbackInfo->env_ = env;
344
345 napi_valuetype valueType1 = napi_undefined;
346 napi_valuetype valueType2 = napi_undefined;
347 NAPI_BT_CALL_RETURN(napi_typeof(env, argv[PARAM1], &valueType1));
348 NAPI_BT_CALL_RETURN(napi_typeof(env, argv[PARAM2], &valueType2));
349 NAPI_BT_RETURN_IF(valueType1 != napi_number && valueType2 != napi_function,
350 "Wrong argument type. Function expected.", napi_invalid_arg);
351
352 napi_create_reference(env, argv[PARAM2], 1, &callbackInfo->callback_);
353
354 NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM1]), "Wrong argument type. Int expected.", napi_invalid_arg);
355
356 std::shared_ptr<NapiSppClient> client = NapiSppClient::clientMap[id];
357 NAPI_BT_RETURN_IF(!client, "client is nullptr.", napi_invalid_arg);
358 NAPI_BT_RETURN_IF(client->sppReadFlag, "client is reading... please off first", napi_invalid_arg);
359 client->sppReadFlag = true;
360 client->callbackInfos_[type] = callbackInfo;
361 NAPI_BT_RETURN_IF(NapiSppCreateThreadSafeFunc(client) != napi_ok, "inner error", napi_invalid_arg);
362 HILOGI("sppRead begin");
363 client->thread_ = std::make_shared<std::thread>(NapiSppClient::SppRead, id);
364 client->thread_->detach();
365 return napi_ok;
366 }
367
On(napi_env env,napi_callback_info info)368 napi_value NapiSppClient::On(napi_env env, napi_callback_info info)
369 {
370 HILOGI("enter");
371 auto status = CheckSppClientOn(env, info);
372 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
373 return NapiGetUndefinedRet(env);
374 }
375
CheckSppClientOff(napi_env env,napi_callback_info info)376 napi_status CheckSppClientOff(napi_env env, napi_callback_info info)
377 {
378 HILOGI("enter");
379 size_t argc = ARGS_SIZE_THREE;
380 napi_value argv[ARGS_SIZE_THREE] = {0};
381 napi_value thisVar = nullptr;
382 int id = -1;
383 std::string type;
384
385 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
386 NAPI_BT_RETURN_IF(
387 (argc != ARGS_SIZE_TWO && argc != ARGS_SIZE_THREE), "Requires 2 or 3 arguments.", napi_invalid_arg);
388 NAPI_BT_RETURN_IF(!ParseString(env, type, argv[PARAM0]),
389 "Wrong argument type. String expected.", napi_invalid_arg);
390 NAPI_BT_RETURN_IF(type != REGISTER_SPP_READ_TYPE, "Invalid type.", napi_invalid_arg);
391
392 NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM1]), "Wrong argument type. Int expected.", napi_invalid_arg);
393
394 std::shared_ptr<NapiSppClient> client = NapiSppClient::clientMap[id];
395 NAPI_BT_RETURN_IF(!client, "client is nullptr.", napi_invalid_arg);
396 NAPI_BT_RETURN_IF(napi_release_threadsafe_function(client->sppReadThreadSafeFunc_, napi_tsfn_abort),
397 "innner error",
398 napi_invalid_arg);
399 client->sppReadThreadSafeFunc_ = nullptr;
400 client->callbackInfos_[type] = nullptr;
401 client->sppReadFlag = false;
402 return napi_ok;
403 }
404
Off(napi_env env,napi_callback_info info)405 napi_value NapiSppClient::Off(napi_env env, napi_callback_info info)
406 {
407 HILOGI("enter");
408 auto status = CheckSppClientOff(env, info);
409 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
410 return NapiGetUndefinedRet(env);
411 }
412
SppRead(int id)413 void NapiSppClient::SppRead(int id)
414 {
415 auto client = clientMap[id];
416 if (client == nullptr || !client->sppReadFlag || client->callbackInfos_[REGISTER_SPP_READ_TYPE] == nullptr) {
417 HILOGE("thread start failed.");
418 return;
419 }
420 std::shared_ptr<InputStream> inputStream = client->client_->GetInputStream();
421 uint8_t buf[SOCKET_BUFFER_SIZE];
422
423 while (true) {
424 HILOGI("thread start.");
425 (void)memset_s(buf, sizeof(buf), 0, sizeof(buf));
426 HILOGI("inputStream.Read start");
427 int ret = inputStream->Read(buf, sizeof(buf));
428 HILOGI("inputStream.Read end");
429 if (ret <= 0) {
430 HILOGI("inputStream.Read failed, ret = %{public}d", ret);
431 return;
432 } else {
433 HILOGI("callback read data to jshap begin");
434 if (client == nullptr || !client->sppReadFlag || !client->callbackInfos_[REGISTER_SPP_READ_TYPE]) {
435 HILOGE("failed");
436 return;
437 }
438 std::shared_ptr<BufferCallbackInfo> callbackInfo =
439 std::static_pointer_cast<BufferCallbackInfo>(client->callbackInfos_[REGISTER_SPP_READ_TYPE]);
440 if (callbackInfo == nullptr) {
441 HILOGE("callbackInfo nullptr");
442 return;
443 }
444
445 std::shared_ptr<SppCallbackBuffer> buffer = std::make_shared<SppCallbackBuffer>();
446 buffer->len_ = ret;
447 if (memcpy_s(buffer->data_, sizeof(buffer->data_), buf, ret) != EOK) {
448 HILOGE("memcpy_s failed!");
449 return;
450 }
451 callbackInfo->PushData(buffer);
452
453 auto status = napi_acquire_threadsafe_function(client->sppReadThreadSafeFunc_);
454 if (status != napi_ok) {
455 HILOGE("napi_acquire_threadsafe_function failed, status: %{public}d", status);
456 return;
457 }
458
459 status = napi_call_threadsafe_function(
460 client->sppReadThreadSafeFunc_, static_cast<void *>(callbackInfo.get()), napi_tsfn_blocking);
461 if (status != napi_ok) {
462 HILOGE("napi_call_threadsafe_function failed, status: %{public}d", status);
463 return;
464 }
465
466 status = napi_release_threadsafe_function(client->sppReadThreadSafeFunc_, napi_tsfn_release);
467 if (status != napi_ok) {
468 HILOGE("napi_release_threadsafe_function failed, status: %{public}d", status);
469 return;
470 }
471 }
472 }
473 return;
474 }
475
WriteDataLoop(std::shared_ptr<OutputStream> outputStream,uint8_t * totalBuf,size_t totalSize)476 static int WriteDataLoop(std::shared_ptr<OutputStream> outputStream, uint8_t* totalBuf, size_t totalSize)
477 {
478 while (totalSize) {
479 int result = outputStream->Write(totalBuf, totalSize);
480 if (result <= 0) {
481 HILOGE("Write failed");
482 return BT_ERR_SPP_IO;
483 }
484 totalSize = totalSize - static_cast<size_t>(result);
485 totalBuf += static_cast<size_t>(result);
486 }
487 return BT_NO_ERROR;
488 }
489
SppWriteAsync(napi_env env,napi_callback_info info)490 napi_value NapiSppClient::SppWriteAsync(napi_env env, napi_callback_info info)
491 {
492 HILOGD("enter");
493 uint8_t* totalBuf = nullptr;
494 size_t totalSize = 0;
495 int id = -1;
496
497 BluetoothHost *host = &BluetoothHost::GetDefaultHost();
498 auto prohibitedTime = host->GetRefusePolicyProhibitedTime();
499 if (prohibitedTime < 0 || prohibitedTime > GetSecondsSince1970ToNow()) {
500 HILOGE("socket refuse because of Refuse Policy");
501 NAPI_BT_ASSERT_RETURN_FALSE(env, false, BT_ERR_INVALID_PARAM);
502 }
503
504 auto status = CheckSppWriteParams(env, info, id, &totalBuf, totalSize);
505 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
506 NAPI_BT_ASSERT_RETURN_UNDEF(env, clientMap[id] > 0, BT_ERR_INTERNAL_ERROR);
507 auto client = clientMap[id];
508 NAPI_BT_ASSERT_RETURN_UNDEF(env, client != nullptr, BT_ERR_INVALID_PARAM);
509 std::shared_ptr<OutputStream> outputStream = client->client_->GetOutputStream();
510
511 auto func = [outputStream, totalBuf, totalSize]() {
512 int err = 0;
513 err = WriteDataLoop(outputStream, totalBuf, totalSize);
514 return NapiAsyncWorkRet(err);
515 };
516
517 auto asyncWork = NapiAsyncWorkFactory::CreateAsyncWork(env, info, func, ASYNC_WORK_NO_NEED_CALLBACK);
518 NAPI_BT_ASSERT_RETURN_UNDEF(env, asyncWork, BT_ERR_INTERNAL_ERROR);
519
520 asyncWork->Run();
521 return asyncWork->GetRet();
522 }
523
CheckSppReadParams(napi_env env,napi_callback_info info,int & id)524 static napi_status CheckSppReadParams(napi_env env, napi_callback_info info, int &id)
525 {
526 size_t argc = ARGS_SIZE_ONE;
527 napi_value argv[ARGS_SIZE_ONE] = {0};
528 napi_value thisVar = nullptr;
529
530 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
531 NAPI_BT_RETURN_IF((argc != ARGS_SIZE_ONE), "Requires 1 arguments.", napi_invalid_arg);
532 napi_valuetype valueType = napi_undefined;
533 NAPI_BT_CALL_RETURN(napi_typeof(env, argv[PARAM0], &valueType));
534 NAPI_BT_RETURN_IF(valueType != napi_number, "Wrong argument type. Function expected.", napi_invalid_arg);
535 NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM0]), "Wrong argument type. int expected.", napi_invalid_arg);
536
537 return napi_ok;
538 }
539
ReadData(std::shared_ptr<InputStream> inputStream,int bufSize,SppCallbackBuffer & sppBuffer)540 static int ReadData(std::shared_ptr<InputStream> inputStream, int bufSize, SppCallbackBuffer &sppBuffer)
541 {
542 (void)memset_s(sppBuffer.data_, bufSize, 0, bufSize);
543 int result = inputStream->Read(reinterpret_cast<uint8_t*>(sppBuffer.data_), bufSize);
544 if (result <= 0) {
545 HILOGE("Read faild");
546 return BT_ERR_SPP_IO;
547 }
548 sppBuffer.len_ = result;
549 return BT_NO_ERROR;
550 }
551
SppReadAsync(napi_env env,napi_callback_info info)552 napi_value NapiSppClient::SppReadAsync(napi_env env, napi_callback_info info)
553 {
554 HILOGD("enter");
555 int id = -1;
556 auto status = CheckSppReadParams(env, info, id);
557 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
558 auto client = clientMap[id];
559 NAPI_BT_ASSERT_RETURN_UNDEF(env, client != nullptr, BT_ERR_INVALID_PARAM);
560 NAPI_BT_ASSERT_RETURN_UNDEF(env, !client->sppReadFlag, BT_ERR_INVALID_PARAM);
561 client->sppReadFlag = true;
562 std::shared_ptr<InputStream> inputStream = client->client_->GetInputStream();
563 auto func = [inputStream, id] {
564 int err = 0;
565 SppCallbackBuffer buffer;
566 err = ReadData(inputStream, SOCKET_BUFFER_SIZE, buffer);
567 auto object = std::make_shared<NapiNativeArrayBuffer>(buffer);
568 auto client = clientMap[id];
569 if (client == nullptr) {
570 HILOGI("client is nullptr");
571 return NapiAsyncWorkRet(BT_ERR_SPP_IO, object);
572 }
573 client->sppReadFlag = false;
574 return NapiAsyncWorkRet(err, object);
575 };
576 auto asyncWork = NapiAsyncWorkFactory::CreateAsyncWork(env, info, func, ASYNC_WORK_NO_NEED_CALLBACK);
577 NAPI_BT_ASSERT_RETURN_UNDEF(env, asyncWork, BT_ERR_INTERNAL_ERROR);
578 asyncWork->Run();
579 return asyncWork->GetRet();
580 }
581
CheckSppFdParams(napi_env env,napi_callback_info info,int & id)582 static napi_status CheckSppFdParams(napi_env env, napi_callback_info info, int &id)
583 {
584 size_t argc = ARGS_SIZE_ONE;
585 napi_value argv[ARGS_SIZE_ONE] = {0};
586
587 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
588 NAPI_BT_RETURN_IF(argc != ARGS_SIZE_ONE, "Requires 1 arguments.", napi_invalid_arg);
589 NAPI_BT_CALL_RETURN(NapiParseInt32(env, argv[PARAM0], id));
590 return napi_ok;
591 }
592
GetDeviceId(napi_env env,napi_callback_info info)593 napi_value NapiSppClient::GetDeviceId(napi_env env, napi_callback_info info)
594 {
595 HILOGD("enter");
596 int id = -1;
597 auto status = CheckSppFdParams(env, info, id);
598 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
599 auto client = clientMap[id];
600 NAPI_BT_ASSERT_RETURN_UNDEF(env, client != nullptr, BT_ERR_INVALID_PARAM);
601 NAPI_BT_ASSERT_RETURN_UNDEF(env, client->client_ != nullptr, BT_ERR_INVALID_PARAM);
602 BluetoothRemoteDevice remoteDevice = client->client_->GetRemoteDevice();
603 std::string addr = remoteDevice.GetDeviceAddr();
604 napi_value result = nullptr;
605 napi_create_string_utf8(env, addr.c_str(), addr.size(), &result);
606 return result;
607 }
608 } // namespace Bluetooth
609 } // namespace OHOS
610