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