• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021-2022 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 
SppConnect(napi_env env,napi_callback_info info)72 napi_value NapiSppClient::SppConnect(napi_env env, napi_callback_info info)
73 {
74     HILOGI("enter");
75     std::string deviceId;
76     SppConnectCallbackInfo *callbackInfo = new SppConnectCallbackInfo();
77     auto status = CheckSppConnectParams(env, info, deviceId, callbackInfo);
78     NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
79 
80     napi_value resource = nullptr;
81     napi_create_string_utf8(env, "SppConnect", NAPI_AUTO_LENGTH, &resource);
82 
83     napi_create_async_work(
84         env, nullptr, resource,
85         [](napi_env env, void* data) {
86             HILOGI("SppConnect execute");
87             SppConnectCallbackInfo* callbackInfo = (SppConnectCallbackInfo*)data;
88             callbackInfo->device_ = std::make_shared<BluetoothRemoteDevice>(callbackInfo->deviceId_, 0);
89             callbackInfo->client_ = std::make_shared<SppClientSocket>(*callbackInfo->device_,
90                 UUID::FromString(callbackInfo->sppOption_->uuid_),
91                 callbackInfo->sppOption_->type_, callbackInfo->sppOption_->secure_);
92             HILOGI("SppConnect client_ constructed");
93             if (callbackInfo->client_->Connect() == BtStatus::BT_SUCC) {
94                 HILOGI("SppConnect successfully");
95                 callbackInfo->errorCode_ = CODE_SUCCESS;
96             } else {
97                 HILOGI("SppConnect failed");
98                 callbackInfo->errorCode_ = CODE_FAILED;
99             }
100         },
101         [](napi_env env, napi_status status, void* data) {
102             HILOGI("SppConnect execute back");
103             SppConnectCallbackInfo* callbackInfo = (SppConnectCallbackInfo*)data;
104             napi_value result[ARGS_SIZE_TWO] = {0};
105             napi_value callback = 0;
106             napi_value undefined = 0;
107             napi_value callResult = 0;
108             napi_get_undefined(env, &undefined);
109 
110             if (callbackInfo->errorCode_ == CODE_SUCCESS) {
111                 HILOGI("SppConnect execute back success");
112                 std::shared_ptr<NapiSppClient> client =  std::make_shared<NapiSppClient>();
113                 client->device_ = callbackInfo->device_;
114                 client->id_ = NapiSppClient::count++;
115                 napi_create_int32(env, client->id_, &result[PARAM1]);
116                 client->client_ = callbackInfo->client_;
117                 clientMap.insert(std::make_pair(client->id_, client));
118                 HILOGI("SppConnect execute back successfully");
119             } else {
120                 napi_get_undefined(env, &result[PARAM1]);
121                 HILOGI("SppConnect execute back failed");
122             }
123 
124             if (callbackInfo->callback_) {
125                 // Callback mode
126                 HILOGI("SppConnect execute Callback mode");
127                 result[PARAM0] = GetCallbackErrorValue(callbackInfo->env_, callbackInfo->errorCode_);
128                 napi_get_reference_value(env, callbackInfo->callback_, &callback);
129                 napi_call_function(env, undefined, callback, ARGS_SIZE_TWO, result, &callResult);
130                 napi_delete_reference(env, callbackInfo->callback_);
131             } else {
132                 if (callbackInfo->errorCode_ == CODE_SUCCESS) {
133                 // Promise mode
134                     HILOGI("SppConnect execute Promise mode successfully");
135                     napi_resolve_deferred(env, callbackInfo->deferred_, result[PARAM1]);
136                 } else {
137                     HILOGI("SppConnect execute Promise mode failed");
138                     napi_reject_deferred(env, callbackInfo->deferred_, result[PARAM1]);
139                 }
140             }
141             napi_delete_async_work(env, callbackInfo->asyncWork_);
142             delete callbackInfo;
143             callbackInfo = nullptr;
144         },
145         (void*)callbackInfo,
146         &callbackInfo->asyncWork_);
147     napi_queue_async_work(env, callbackInfo->asyncWork_);
148     return NapiGetUndefinedRet(env);
149 }
150 
CheckSppCloseClientSocketParams(napi_env env,napi_callback_info info,int & id)151 static napi_status CheckSppCloseClientSocketParams(napi_env env, napi_callback_info info, int &id)
152 {
153     HILOGI("enter");
154     size_t argc = ARGS_SIZE_ONE;
155     napi_value argv[ARGS_SIZE_ONE] = {0};
156     napi_value thisVar = nullptr;
157 
158     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
159     NAPI_BT_RETURN_IF((argc != ARGS_SIZE_ONE), "Requires 1 arguments.", napi_invalid_arg);
160     NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM0]), "Wrong argument type. int expected.", napi_invalid_arg);
161     return napi_ok;
162 }
163 
SppCloseClientSocket(napi_env env,napi_callback_info info)164 napi_value NapiSppClient::SppCloseClientSocket(napi_env env, napi_callback_info info)
165 {
166     HILOGI("enter");
167     std::shared_ptr<NapiSppClient> client = nullptr;
168     int id =  -1;
169     bool isOK = false;
170     auto status = CheckSppCloseClientSocketParams(env, info, id);
171     NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
172 
173     if (clientMap[id]) {
174         client = clientMap[id];
175     } else {
176         HILOGE("no such key in map.");
177         return NapiGetUndefinedRet(env);
178     }
179 
180     if (client->client_) {
181         client->client_->Close();
182         isOK = true;
183     }
184     clientMap.erase(id);
185     return NapiGetBooleanRet(env, isOK);
186 }
187 
CheckSppWriteParams(napi_env env,napi_callback_info info,int & id,char ** totalBuf,size_t & totalSize)188 static napi_status CheckSppWriteParams(
189     napi_env env, napi_callback_info info, int &id, char** totalBuf, size_t &totalSize)
190 {
191     HILOGI("enter");
192     size_t argc = ARGS_SIZE_TWO;
193     napi_value argv[ARGS_SIZE_TWO] = {0};
194     napi_value thisVar = nullptr;
195 
196     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
197     NAPI_BT_RETURN_IF((argc != ARGS_SIZE_TWO), "Requires 2 arguments.", napi_invalid_arg);
198     NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM0]), "Wrong argument type. int expected.", napi_invalid_arg);
199     NAPI_BT_RETURN_IF(!ParseArrayBuffer(env, (uint8_t**)(totalBuf), totalSize, argv[PARAM1]),
200         "ParseArrayBuffer failed.", napi_invalid_arg);
201     return napi_ok;
202 }
203 
SppWrite(napi_env env,napi_callback_info info)204 napi_value NapiSppClient::SppWrite(napi_env env, napi_callback_info info)
205 {
206     HILOGI("enter");
207     char* totalBuf = nullptr;
208     size_t totalSize = 0;
209     bool isOK = false;
210     int id = -1;
211 
212     auto status = CheckSppWriteParams(env, info, id, &totalBuf, totalSize);
213     NAPI_BT_ASSERT_RETURN_FALSE(env, status == napi_ok, BT_ERR_INVALID_PARAM);
214     NAPI_BT_ASSERT_RETURN_FALSE(env, clientMap[id] > 0, BT_ERR_INTERNAL_ERROR);
215     OutputStream outputStream = clientMap[id]->client_->GetOutputStream();
216     while (totalSize) {
217         int result = outputStream.Write(totalBuf, totalSize);
218         NAPI_BT_ASSERT_RETURN_FALSE(env, result > 0, BT_ERR_SPP_IO);
219         totalSize = totalSize - result;
220         totalBuf += result;
221         isOK = true;
222     }
223     return NapiGetBooleanRet(env, isOK);
224 }
225 
CheckSppClientOn(napi_env env,napi_callback_info info)226 napi_status CheckSppClientOn(napi_env env, napi_callback_info info)
227 {
228     HILOGI("enter");
229     size_t argc = ARGS_SIZE_THREE;
230     napi_value argv[ARGS_SIZE_THREE] = {0};
231     napi_value thisVar = nullptr;
232     int id = -1;
233     std::string type;
234     std::shared_ptr<BluetoothCallbackInfo> callbackInfo;
235 
236     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
237     NAPI_BT_RETURN_IF((argc != ARGS_SIZE_THREE), "Requires 3 arguments.", napi_invalid_arg);
238     NAPI_BT_RETURN_IF(!ParseString(env, type, argv[PARAM0]),
239         "Wrong argument type. String expected.", napi_invalid_arg);
240     NAPI_BT_RETURN_IF(type.c_str() != STR_BT_SPP_READ, "Invalid type.", napi_invalid_arg);
241 
242     callbackInfo = std::make_shared<BufferCallbackInfo>();
243     callbackInfo->env_ = env;
244 
245     napi_valuetype valueType1 = napi_undefined;
246     napi_valuetype valueType2 = napi_undefined;
247     NAPI_BT_CALL_RETURN(napi_typeof(env, argv[PARAM1], &valueType1));
248     NAPI_BT_CALL_RETURN(napi_typeof(env, argv[PARAM2], &valueType2));
249     NAPI_BT_RETURN_IF(valueType1 != napi_number && valueType2 != napi_function,
250         "Wrong argument type. Function expected.", napi_invalid_arg);
251 
252     napi_create_reference(env, argv[PARAM2], 1, &callbackInfo->callback_);
253 
254     NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM1]), "Wrong argument type. Int expected.", napi_invalid_arg);
255 
256     std::shared_ptr<NapiSppClient> client = NapiSppClient::clientMap[id];
257     NAPI_BT_RETURN_IF(!client, "client is nullptr.", napi_invalid_arg);
258     client->sppReadFlag = true;
259     client->callbackInfos_[type] = callbackInfo;
260     HILOGI("sppRead begin");
261     client->thread_ = std::make_shared<std::thread>(NapiSppClient::SppRead, id);
262     client->thread_->detach();
263     return napi_ok;
264 }
265 
On(napi_env env,napi_callback_info info)266 napi_value NapiSppClient::On(napi_env env, napi_callback_info info)
267 {
268     HILOGI("enter");
269     auto status = CheckSppClientOn(env, info);
270     NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
271     return NapiGetUndefinedRet(env);
272 }
273 
CheckSppClientOff(napi_env env,napi_callback_info info)274 napi_status CheckSppClientOff(napi_env env, napi_callback_info info)
275 {
276     HILOGI("enter");
277     size_t argc = ARGS_SIZE_THREE;
278     napi_value argv[ARGS_SIZE_THREE] = {0};
279     napi_value thisVar = nullptr;
280     int id = -1;
281     std::string type;
282 
283     NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
284     NAPI_BT_RETURN_IF((argc != ARGS_SIZE_THREE), "Requires 3 arguments.", napi_invalid_arg);
285     NAPI_BT_RETURN_IF(!ParseString(env, type, argv[PARAM0]),
286         "Wrong argument type. String expected.", napi_invalid_arg);
287     NAPI_BT_RETURN_IF(type.c_str() != STR_BT_SPP_READ, "Invalid type.", napi_invalid_arg);
288 
289     NAPI_BT_RETURN_IF(!ParseInt32(env, id, argv[PARAM1]), "Wrong argument type. Int expected.", napi_invalid_arg);
290 
291     std::shared_ptr<NapiSppClient> client = NapiSppClient::clientMap[id];
292     NAPI_BT_RETURN_IF(!client, "client is nullptr.", napi_invalid_arg);
293     client->callbackInfos_[type] = nullptr;
294     client->sppReadFlag = false;
295     sleep(sleepTime);
296     return napi_ok;
297 }
298 
Off(napi_env env,napi_callback_info info)299 napi_value NapiSppClient::Off(napi_env env, napi_callback_info info)
300 {
301     HILOGI("enter");
302     auto status = CheckSppClientOff(env, info);
303     NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
304     return NapiGetUndefinedRet(env);
305 }
306 
SppRead(int id)307 void NapiSppClient::SppRead(int id)
308 {
309     if (clientMap[id] == nullptr || !clientMap[id]->sppReadFlag ||
310         clientMap[id]->callbackInfos_[STR_BT_SPP_READ] == nullptr) {
311         HILOGE("thread start failed.");
312         return;
313     }
314     InputStream inputStream = clientMap[id]->client_->GetInputStream();
315     char buf[SOCKET_BUFFER_SIZE];
316     int ret = 0;
317 
318     while (true) {
319         HILOGI("thread start.");
320         (void)memset_s(buf, sizeof(buf), 0, sizeof(buf));
321         HILOGI("inputStream.Read start");
322         ret = inputStream.Read(buf, sizeof(buf));
323         HILOGI("inputStream.Read end");
324         if (ret <= 0) {
325             HILOGI("inputStream.Read failed, ret = %{public}d", ret);
326             return;
327         } else {
328             HILOGI("callback read data to jshap begin");
329             if (clientMap[id] == nullptr || !clientMap[id]->sppReadFlag ||
330                 clientMap[id]->callbackInfos_[STR_BT_SPP_READ] == nullptr) {
331                 HILOGE("failed");
332                 return;
333             }
334             std::shared_ptr<BufferCallbackInfo> callbackInfo =
335                 std::static_pointer_cast<BufferCallbackInfo>(clientMap[id]->callbackInfos_[STR_BT_SPP_READ]);
336 
337             callbackInfo->info_ = ret;
338             if (memcpy_s(callbackInfo->buffer_, sizeof(callbackInfo->buffer_), buf, ret) != EOK) {
339                 HILOGE("memcpy_s failed!");
340                 return;
341             }
342 
343             uv_loop_s *loop = nullptr;
344             napi_get_uv_event_loop(callbackInfo->env_, &loop);
345             uv_work_t *work = new uv_work_t;
346             work->data = (void*)callbackInfo.get();
347 
348             uv_queue_work(
349                 loop,
350                 work,
351                 [](uv_work_t *work) {},
352                 [](uv_work_t *work, int status) {
353                     BufferCallbackInfo *callbackInfo = (BufferCallbackInfo *)work->data;
354                     int size = callbackInfo->info_;
355                     if (size < 0 || size > SOCKET_BUFFER_SIZE) {
356                         HILOGE("malloc size error");
357                         delete work;
358                         work = nullptr;
359                         return;
360                     }
361                     uint8_t* totalBuf = (uint8_t*) malloc(size);
362                     if (totalBuf == nullptr) {
363                         HILOGE("malloc failed");
364                         delete work;
365                         work = nullptr;
366                         return;
367                     }
368                     if (memcpy_s(totalBuf, size, callbackInfo->buffer_, size) != EOK) {
369                         HILOGE("memcpy_s failed!");
370                     }
371                     napi_value result = nullptr;
372                     uint8_t* bufferData = nullptr;
373                     napi_create_arraybuffer(callbackInfo->env_, size, (void**)&bufferData, &result);
374                     if (memcpy_s(bufferData, size, totalBuf, size) != EOK) {
375                         HILOGE("memcpy_s failed!");
376                     }
377                     free(totalBuf);
378 
379                     napi_value callback = nullptr;
380                     napi_value undefined = nullptr;
381                     napi_value callResult = nullptr;
382                     napi_get_undefined(callbackInfo->env_, &undefined);
383                     napi_get_reference_value(callbackInfo->env_, callbackInfo->callback_, &callback);
384                     napi_call_function(callbackInfo->env_, undefined, callback, ARGS_SIZE_ONE, &result, &callResult);
385                     delete work;
386                     work = nullptr;
387                 }
388             );
389         }
390     }
391     return;
392 }
393 } // namespace Bluetooth
394 } // namespace OHOS