• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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