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, ×) < 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