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