• 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 #include "bluetooth_errorcode.h"
16 #include "napi_bluetooth_spp_client.h"
17 #include "napi_bluetooth_error.h"
18 #include "napi_bluetooth_utils.h"
19 #include "securec.h"
20 #include <limits>
21 #include <unistd.h>
22 #include <uv.h>
23 
24 namespace OHOS {
25 namespace Bluetooth {
26 std::map<int, std::shared_ptr<NapiSppClient>> NapiSppClient::clientMap;
27 int NapiSppClient::count = 0;
28 const int SOCKET_BUFFER_SIZE = 1024;
29 
CheckSppConnectParams(napi_env env,napi_callback_info info,std::string & deviceId,SppConnectCallbackInfo * callbackInfo)30 static napi_status CheckSppConnectParams(
31     napi_env env, napi_callback_info info, std::string &deviceId, SppConnectCallbackInfo *callbackInfo)
32 {
33     HILOGI("enter");
34     size_t argc = ARGS_SIZE_THREE;
35     napi_value argv[ARGS_SIZE_THREE] = {0};
36 
37     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr));
38     NAPI_BT_RETURN_IF((argc != ARGS_SIZE_THREE && argc != ARGS_SIZE_THREE - CALLBACK_SIZE),
39         "Requires 2 or 3 arguments.", napi_invalid_arg);
40     NAPI_BT_RETURN_IF(!ParseString(env, deviceId, argv[PARAM0]),
41         "Wrong argument type. String expected.", napi_invalid_arg);
42 
43     callbackInfo->env_ = env;
44     callbackInfo->sppOption_ = GetSppOptionFromJS(env, argv[PARAM1]);
45     NAPI_BT_RETURN_IF((callbackInfo->sppOption_ == nullptr), "GetSppOptionFromJS faild.", napi_invalid_arg);
46     callbackInfo->deviceId_ = deviceId;
47 
48     napi_value promise = nullptr;
49 
50     if (argc == ARGS_SIZE_THREE) {
51         // Callback mode
52         HILOGI("callback mode");
53         napi_valuetype valueType = napi_undefined;
54         napi_typeof(env, argv[PARAM2], &valueType);
55         if (valueType != napi_function) {
56             HILOGE("Wrong argument type. Function expected.");
57             delete callbackInfo;
58             callbackInfo = nullptr;
59             return napi_invalid_arg;
60         }
61         napi_create_reference(env, argv[PARAM2], 1, &callbackInfo->callback_);
62         napi_get_undefined(env, &promise);
63     } else {
64         // Promise mode
65         HILOGI("promise mode");
66         napi_create_promise(env, &callbackInfo->deferred_, &promise);
67     }
68     return napi_ok;
69 }
70 
GetSppOptionFromJS(napi_env env,napi_value object)71 std::shared_ptr<SppOption> GetSppOptionFromJS(napi_env env, napi_value object)
72 {
73     std::shared_ptr<SppOption> sppOption = std::make_shared<SppOption>();
74     napi_value propertyNameValue = nullptr;
75     napi_value value = nullptr;
76 
77     napi_create_string_utf8(env, "uuid", NAPI_AUTO_LENGTH, &propertyNameValue);
78     napi_get_property(env, object, propertyNameValue, &value);
79     bool isSuccess = ParseString(env, sppOption->uuid_, value);
80     if (!isSuccess || (!IsValidUuid(sppOption->uuid_))) {
81         HILOGE("Parse UUID faild.");
82         return nullptr;
83     }
84     HILOGI("uuid is %{public}s", sppOption->uuid_.c_str());
85 
86     napi_create_string_utf8(env, "secure", NAPI_AUTO_LENGTH, &propertyNameValue);
87     napi_get_property(env, object, propertyNameValue, &value);
88     ParseBool(env, sppOption->secure_, value);
89     HILOGI("secure is %{public}d", sppOption->secure_);
90 
91     int type = 0;
92     napi_create_string_utf8(env, "type", NAPI_AUTO_LENGTH, &propertyNameValue);
93     napi_get_property(env, object, propertyNameValue, &value);
94     ParseInt32(env, type, value);
95     sppOption->type_ = BtSocketType(type);
96     HILOGI("uuid: %{public}s, secure: %{public}d, type: %{public}d",
97         sppOption->uuid_.c_str(), sppOption->secure_, sppOption->type_);
98     return sppOption;
99 }
100 
SppConnect(napi_env env,napi_callback_info info)101 napi_value NapiSppClient::SppConnect(napi_env env, napi_callback_info info)
102 {
103     HILOGI("enter");
104     std::string deviceId;
105     SppConnectCallbackInfo *callbackInfo = new SppConnectCallbackInfo();
106     auto status = CheckSppConnectParams(env, info, deviceId, callbackInfo);
107     NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
108 
109     napi_value resource = nullptr;
110     napi_create_string_utf8(env, "SppConnect", NAPI_AUTO_LENGTH, &resource);
111 
112     napi_create_async_work(
113         env, nullptr, resource,
114         [](napi_env env, void* data) {
115             HILOGI("SppConnect execute");
116             SppConnectCallbackInfo* callbackInfo = static_cast<SppConnectCallbackInfo*>(data);
117             callbackInfo->device_ = std::make_shared<BluetoothRemoteDevice>(callbackInfo->deviceId_, 0);
118             callbackInfo->client_ = std::make_shared<ClientSocket>(*callbackInfo->device_,
119                 UUID::FromString(callbackInfo->sppOption_->uuid_),
120                 callbackInfo->sppOption_->type_, callbackInfo->sppOption_->secure_);
121             HILOGI("SppConnect client_ constructed");
122             callbackInfo->errorCode_ = callbackInfo->client_->Connect(SPP_SOCKET_PSM_VALUE);
123             if (callbackInfo->errorCode_ == BtStatus::BT_SUCCESS) {
124                 HILOGI("SppConnect successfully");
125                 callbackInfo->errorCode_ = CODE_SUCCESS;
126             } else {
127                 HILOGE("SppConnect failed");
128             }
129         },
130         [](napi_env env, napi_status status, void* data) {
131             HILOGI("SppConnect execute back");
132             SppConnectCallbackInfo* callbackInfo = static_cast<SppConnectCallbackInfo*>(data);
133             napi_value result[ARGS_SIZE_TWO] = {0};
134             napi_value callback = 0;
135             napi_value undefined = 0;
136             napi_value callResult = 0;
137             napi_get_undefined(env, &undefined);
138 
139             if (callbackInfo->errorCode_ == CODE_SUCCESS) {
140                 HILOGI("SppConnect execute back success");
141                 std::shared_ptr<NapiSppClient> client =  std::make_shared<NapiSppClient>();
142                 client->device_ = callbackInfo->device_;
143                 client->id_ = NapiSppClient::count++;
144                 napi_create_int32(env, client->id_, &result[PARAM1]);
145                 client->client_ = callbackInfo->client_;
146                 clientMap.insert(std::make_pair(client->id_, client));
147                 HILOGI("SppConnect execute back successfully");
148             } else {
149                 napi_get_undefined(env, &result[PARAM1]);
150                 HILOGI("SppConnect execute back failed");
151             }
152 
153             if (callbackInfo->callback_) {
154                 // Callback mode
155                 HILOGI("SppConnect execute Callback mode");
156                 result[PARAM0] = GetCallbackErrorValue(callbackInfo->env_, callbackInfo->errorCode_);
157                 napi_get_reference_value(env, callbackInfo->callback_, &callback);
158                 napi_call_function(env, undefined, callback, ARGS_SIZE_TWO, result, &callResult);
159                 napi_delete_reference(env, callbackInfo->callback_);
160             } else {
161                 if (callbackInfo->errorCode_ == CODE_SUCCESS) {
162                 // Promise mode
163                     HILOGI("SppConnect execute Promise mode successfully");
164                     napi_resolve_deferred(env, callbackInfo->deferred_, result[PARAM1]);
165                 } else {
166                     HILOGI("SppConnect execute Promise mode failed");
167                     napi_reject_deferred(env, callbackInfo->deferred_, result[PARAM1]);
168                 }
169             }
170             napi_delete_async_work(env, callbackInfo->asyncWork_);
171             delete callbackInfo;
172             callbackInfo = nullptr;
173         },
174         static_cast<void*>(callbackInfo), &callbackInfo->asyncWork_);
175     if (napi_queue_async_work(env, callbackInfo->asyncWork_) != napi_ok) {
176         HILOGE("SppConnect napi_queue_async_work failed");
177         delete callbackInfo;
178         callbackInfo = nullptr;
179     }
180     return NapiGetUndefinedRet(env);
181 }
182 
CheckSppCloseClientSocketParams(napi_env env,napi_callback_info info,int & id)183 static napi_status CheckSppCloseClientSocketParams(napi_env env, napi_callback_info info, int &id)
184 {
185     HILOGI("enter");
186     size_t argc = ARGS_SIZE_ONE;
187     napi_value argv[ARGS_SIZE_ONE] = {0};
188     napi_value thisVar = nullptr;
189 
190     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
191     NAPI_BT_RETURN_IF((argc != ARGS_SIZE_ONE), "Requires 1 arguments.", napi_invalid_arg);
192     NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM0]), "Wrong argument type. int expected.", napi_invalid_arg);
193     return napi_ok;
194 }
195 
SppCloseClientSocket(napi_env env,napi_callback_info info)196 napi_value NapiSppClient::SppCloseClientSocket(napi_env env, napi_callback_info info)
197 {
198     HILOGI("enter");
199     std::shared_ptr<NapiSppClient> client = nullptr;
200     int id =  -1;
201     bool isOK = false;
202     auto status = CheckSppCloseClientSocketParams(env, info, id);
203     NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
204 
205     if (clientMap[id]) {
206         client = clientMap[id];
207     } else {
208         HILOGE("no such key in map.");
209         return NapiGetUndefinedRet(env);
210     }
211 
212     if (client->client_) {
213         client->client_->Close();
214         isOK = true;
215     }
216     clientMap.erase(id);
217     return NapiGetBooleanRet(env, isOK);
218 }
219 
CheckSppWriteParams(napi_env env,napi_callback_info info,int & id,uint8_t ** totalBuf,size_t & totalSize)220 static napi_status CheckSppWriteParams(
221     napi_env env, napi_callback_info info, int &id, uint8_t** totalBuf, size_t &totalSize)
222 {
223     HILOGI("enter");
224     size_t argc = ARGS_SIZE_TWO;
225     napi_value argv[ARGS_SIZE_TWO] = {0};
226     napi_value thisVar = nullptr;
227 
228     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
229     NAPI_BT_RETURN_IF((argc != ARGS_SIZE_TWO), "Requires 2 arguments.", napi_invalid_arg);
230     NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM0]), "Wrong argument type. int expected.", napi_invalid_arg);
231     NAPI_BT_RETURN_IF(!ParseArrayBuffer(env, totalBuf, totalSize, argv[PARAM1]),
232         "ParseArrayBuffer failed.", napi_invalid_arg);
233     return napi_ok;
234 }
235 
SppWrite(napi_env env,napi_callback_info info)236 napi_value NapiSppClient::SppWrite(napi_env env, napi_callback_info info)
237 {
238     HILOGI("enter");
239     uint8_t* totalBuf = nullptr;
240     size_t totalSize = 0;
241     bool isOK = false;
242     int id = -1;
243 
244     auto status = CheckSppWriteParams(env, info, id, &totalBuf, totalSize);
245     NAPI_BT_ASSERT_RETURN_FALSE(env, status == napi_ok, BT_ERR_INVALID_PARAM);
246     NAPI_BT_ASSERT_RETURN_FALSE(env, clientMap[id] > 0, BT_ERR_INTERNAL_ERROR);
247     std::shared_ptr<OutputStream> outputStream = clientMap[id]->client_->GetOutputStream();
248     while (totalSize) {
249         int result = outputStream->Write(totalBuf, totalSize);
250         NAPI_BT_ASSERT_RETURN_FALSE(env, result > 0, BT_ERR_SPP_IO);
251         totalSize = totalSize - static_cast<size_t>(result);
252         totalBuf += static_cast<size_t>(result);
253         isOK = true;
254     }
255     return NapiGetBooleanRet(env, isOK);
256 }
257 
NapiThreadSafeFuncCallJs(napi_env,napi_value jsCallback,void * context,void * data)258 static void NapiThreadSafeFuncCallJs(napi_env, napi_value jsCallback, void *context, void *data)
259 {
260     BufferCallbackInfo *callbackInfo = static_cast<BufferCallbackInfo *>(data);
261     std::shared_ptr<SppCallbackBuffer> buffer = callbackInfo->PopData();
262     if (buffer == nullptr) {
263         HILOGE("callbackInfo->PopData return nullptr");
264         return;
265     }
266     if (buffer->len_ < 0 || buffer->len_ > SOCKET_BUFFER_SIZE) {
267         HILOGE("buffer->len_ invalid");
268         return;
269     }
270 
271     napi_value result = nullptr;
272     uint8_t *bufferData = nullptr;
273     napi_create_arraybuffer(callbackInfo->env_, buffer->len_, (void **)&bufferData, &result);
274     if (memcpy_s(bufferData, buffer->len_, buffer->data_, buffer->len_) != EOK) {
275         HILOGE("memcpy_s failed!");
276         return;
277     }
278 
279     napi_value undefined = nullptr;
280     napi_value callResult = nullptr;
281     napi_get_undefined(callbackInfo->env_, &undefined);
282     napi_call_function(callbackInfo->env_, undefined, jsCallback, ARGS_SIZE_ONE, &result, &callResult);
283 }
284 
NapiSppCreateThreadSafeFunc(const std::shared_ptr<NapiSppClient> & client)285 static napi_status NapiSppCreateThreadSafeFunc(const std::shared_ptr<NapiSppClient> &client)
286 {
287     napi_value name;
288     napi_threadsafe_function tsfn;
289     const size_t maxQueueSize = 0;  // 0 means no limited
290     const size_t initialThreadCount = 1;
291     napi_value callback = nullptr;
292     auto callbackInfo = client->callbackInfos_[REGISTER_SPP_READ_TYPE];
293     NAPI_BT_CALL_RETURN(napi_create_string_utf8(callbackInfo->env_, "SppRead", NAPI_AUTO_LENGTH, &name));
294     NAPI_BT_CALL_RETURN(napi_get_reference_value(callbackInfo->env_, callbackInfo->callback_, &callback));
295     NAPI_BT_CALL_RETURN(napi_create_threadsafe_function(callbackInfo->env_, callback, nullptr,
296         name, maxQueueSize, initialThreadCount, nullptr, nullptr, nullptr, NapiThreadSafeFuncCallJs, &tsfn));
297 
298     client->sppReadThreadSafeFunc_ = tsfn;
299     return napi_ok;
300 }
301 
CheckSppClientOn(napi_env env,napi_callback_info info)302 napi_status CheckSppClientOn(napi_env env, napi_callback_info info)
303 {
304     HILOGI("enter");
305     size_t argc = ARGS_SIZE_THREE;
306     napi_value argv[ARGS_SIZE_THREE] = {0};
307     napi_value thisVar = nullptr;
308     int id = -1;
309     std::string type;
310 
311     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
312     NAPI_BT_RETURN_IF((argc != ARGS_SIZE_THREE), "Requires 3 arguments.", napi_invalid_arg);
313     NAPI_BT_RETURN_IF(!ParseString(env, type, argv[PARAM0]),
314         "Wrong argument type. String expected.", napi_invalid_arg);
315     NAPI_BT_RETURN_IF(type.c_str() != REGISTER_SPP_READ_TYPE, "Invalid type.", napi_invalid_arg);
316 
317     std::shared_ptr<BluetoothCallbackInfo> callbackInfo = std::make_shared<BufferCallbackInfo>();
318     callbackInfo->env_ = env;
319 
320     napi_valuetype valueType1 = napi_undefined;
321     napi_valuetype valueType2 = napi_undefined;
322     NAPI_BT_CALL_RETURN(napi_typeof(env, argv[PARAM1], &valueType1));
323     NAPI_BT_CALL_RETURN(napi_typeof(env, argv[PARAM2], &valueType2));
324     NAPI_BT_RETURN_IF(valueType1 != napi_number && valueType2 != napi_function,
325         "Wrong argument type. Function expected.", napi_invalid_arg);
326 
327     napi_create_reference(env, argv[PARAM2], 1, &callbackInfo->callback_);
328 
329     NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM1]), "Wrong argument type. Int expected.", napi_invalid_arg);
330 
331     std::shared_ptr<NapiSppClient> client = NapiSppClient::clientMap[id];
332     NAPI_BT_RETURN_IF(!client, "client is nullptr.", napi_invalid_arg);
333     NAPI_BT_RETURN_IF(client->sppReadFlag, "client is reading... please off first", napi_invalid_arg);
334     client->sppReadFlag = true;
335     client->callbackInfos_[type] = callbackInfo;
336     NAPI_BT_RETURN_IF(NapiSppCreateThreadSafeFunc(client) != napi_ok, "inner error", napi_invalid_arg);
337     HILOGI("sppRead begin");
338     client->thread_ = std::make_shared<std::thread>(NapiSppClient::SppRead, id);
339     client->thread_->detach();
340     return napi_ok;
341 }
342 
On(napi_env env,napi_callback_info info)343 napi_value NapiSppClient::On(napi_env env, napi_callback_info info)
344 {
345     HILOGI("enter");
346     auto status = CheckSppClientOn(env, info);
347     NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
348     return NapiGetUndefinedRet(env);
349 }
350 
CheckSppClientOff(napi_env env,napi_callback_info info)351 napi_status CheckSppClientOff(napi_env env, napi_callback_info info)
352 {
353     HILOGI("enter");
354     size_t argc = ARGS_SIZE_THREE;
355     napi_value argv[ARGS_SIZE_THREE] = {0};
356     napi_value thisVar = nullptr;
357     int id = -1;
358     std::string type;
359 
360     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
361     NAPI_BT_RETURN_IF(
362         (argc != ARGS_SIZE_TWO && argc != ARGS_SIZE_THREE), "Requires 2 or 3 arguments.", napi_invalid_arg);
363     NAPI_BT_RETURN_IF(!ParseString(env, type, argv[PARAM0]),
364                       "Wrong argument type. String expected.", napi_invalid_arg);
365     NAPI_BT_RETURN_IF(type.c_str() != REGISTER_SPP_READ_TYPE, "Invalid type.", napi_invalid_arg);
366 
367     NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM1]), "Wrong argument type. Int expected.", napi_invalid_arg);
368 
369     std::shared_ptr<NapiSppClient> client = NapiSppClient::clientMap[id];
370     NAPI_BT_RETURN_IF(!client, "client is nullptr.", napi_invalid_arg);
371     NAPI_BT_RETURN_IF(napi_release_threadsafe_function(client->sppReadThreadSafeFunc_, napi_tsfn_abort),
372         "innner error",
373         napi_invalid_arg);
374     client->sppReadThreadSafeFunc_ = nullptr;
375     client->callbackInfos_[type] = nullptr;
376     client->sppReadFlag = false;
377     return napi_ok;
378 }
379 
Off(napi_env env,napi_callback_info info)380 napi_value NapiSppClient::Off(napi_env env, napi_callback_info info)
381 {
382     HILOGI("enter");
383     auto status = CheckSppClientOff(env, info);
384     NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
385     return NapiGetUndefinedRet(env);
386 }
387 
SppRead(int id)388 void NapiSppClient::SppRead(int id)
389 {
390     auto client = clientMap[id];
391     if (client == nullptr || !client->sppReadFlag || client->callbackInfos_[REGISTER_SPP_READ_TYPE] == nullptr) {
392         HILOGE("thread start failed.");
393         return;
394     }
395     std::shared_ptr<InputStream> inputStream = client->client_->GetInputStream();
396     uint8_t buf[SOCKET_BUFFER_SIZE];
397 
398     while (true) {
399         HILOGI("thread start.");
400         (void)memset_s(buf, sizeof(buf), 0, sizeof(buf));
401         HILOGI("inputStream.Read start");
402         int ret = inputStream->Read(buf, sizeof(buf));
403         HILOGI("inputStream.Read end");
404         if (ret <= 0) {
405             HILOGI("inputStream.Read failed, ret = %{public}d", ret);
406             return;
407         } else {
408             HILOGI("callback read data to jshap begin");
409             if (client == nullptr || !client->sppReadFlag || !client->callbackInfos_[REGISTER_SPP_READ_TYPE]) {
410                 HILOGE("failed");
411                 return;
412             }
413             std::shared_ptr<BufferCallbackInfo> callbackInfo =
414                 std::static_pointer_cast<BufferCallbackInfo>(client->callbackInfos_[REGISTER_SPP_READ_TYPE]);
415             if (callbackInfo == nullptr) {
416                 HILOGE("callbackInfo nullptr");
417                 return;
418             }
419 
420             std::shared_ptr<SppCallbackBuffer> buffer = std::make_shared<SppCallbackBuffer>();
421             buffer->len_ = ret;
422             if (memcpy_s(buffer->data_, sizeof(buffer->data_), buf, ret) != EOK) {
423                 HILOGE("memcpy_s failed!");
424                 return;
425             }
426             callbackInfo->PushData(buffer);
427 
428             auto status = napi_acquire_threadsafe_function(client->sppReadThreadSafeFunc_);
429             if (status != napi_ok) {
430                 HILOGE("napi_acquire_threadsafe_function failed, status: %{public}d", status);
431                 return;
432             }
433 
434             status = napi_call_threadsafe_function(
435                 client->sppReadThreadSafeFunc_, static_cast<void *>(callbackInfo.get()), napi_tsfn_blocking);
436             if (status != napi_ok) {
437                 HILOGE("napi_call_threadsafe_function failed, status: %{public}d", status);
438                 return;
439             }
440 
441             status = napi_release_threadsafe_function(client->sppReadThreadSafeFunc_, napi_tsfn_release);
442             if (status != napi_ok) {
443                 HILOGE("napi_release_threadsafe_function failed, status: %{public}d", status);
444                 return;
445             }
446         }
447     }
448     return;
449 }
450 } // namespace Bluetooth
451 } // namespace OHOS