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