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_ble_utils"
17 #endif
18 
19 #include "napi_bluetooth_utils.h"
20 #include "napi_bluetooth_ble_utils.h"
21 #include <algorithm>
22 #include <functional>
23 #include <optional>
24 #include "bluetooth_errorcode.h"
25 #include "bluetooth_log.h"
26 #include "napi/native_api.h"
27 #include "napi/native_node_api.h"
28 #include "napi_bluetooth_error.h"
29 #include "securec.h"
30 
31 namespace OHOS {
32 namespace Bluetooth {
33 using namespace std;
ConvertGattServiceToJS(napi_env env,napi_value result,GattService & service)34 void ConvertGattServiceToJS(napi_env env, napi_value result, GattService& service)
35 {
36     napi_value serviceUuid;
37     napi_create_string_utf8(env, service.GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH, &serviceUuid);
38     napi_set_named_property(env, result, "serviceUuid", serviceUuid);
39 
40     napi_value isPrimary;
41     napi_get_boolean(env, service.IsPrimary(), &isPrimary);
42     napi_set_named_property(env, result, "isPrimary", isPrimary);
43     HILOGI("uuid: %{public}s, isPrimary: %{public}d", service.GetUuid().ToString().c_str(), service.IsPrimary());
44 
45     napi_value characteristics;
46     napi_create_array(env, &characteristics);
47     ConvertBLECharacteristicVectorToJS(env, characteristics, service.GetCharacteristics());
48     napi_set_named_property(env, result, "characteristics", characteristics);
49 
50     napi_value includeServices;
51     napi_create_array(env, &includeServices);
52     vector<GattService> services;
53     vector<std::reference_wrapper<GattService>> srvs = service.GetIncludedServices();
54     for (auto &srv : srvs) {
55         services.push_back(srv.get());
56     }
57     ConvertGattServiceVectorToJS(env, includeServices, services);
58     napi_set_named_property(env, result, "includeServices", includeServices);
59 }
60 
ConvertGattServiceVectorToJS(napi_env env,napi_value result,vector<GattService> & services)61 void ConvertGattServiceVectorToJS(napi_env env, napi_value result, vector<GattService>& services)
62 {
63     HILOGI("enter");
64     size_t idx = 0;
65 
66     if (services.empty()) {
67         return;
68     }
69     HILOGI("size: %{public}zu", services.size());
70     for (auto& service : services) {
71         napi_value obj = nullptr;
72         napi_create_object(env, &obj);
73         ConvertGattServiceToJS(env, obj, service);
74         napi_set_element(env, result, idx, obj);
75         idx++;
76     }
77 }
78 
ConvertBLECharacteristicVectorToJS(napi_env env,napi_value result,vector<GattCharacteristic> & characteristics)79 void ConvertBLECharacteristicVectorToJS(napi_env env, napi_value result,
80     vector<GattCharacteristic>& characteristics)
81 {
82     HILOGI("size: %{public}zu", characteristics.size());
83     size_t idx = 0;
84     if (characteristics.empty()) {
85         return;
86     }
87 
88     for (auto &characteristic : characteristics) {
89         napi_value obj = nullptr;
90         napi_create_object(env, &obj);
91         ConvertBLECharacteristicToJS(env, obj, characteristic);
92         napi_set_element(env, result, idx, obj);
93         idx++;
94     }
95 }
96 
HasProperty(int properties,int propertyMask)97 bool HasProperty(int properties, int propertyMask)
98 {
99     if (properties < 0 || propertyMask < 0) {
100         HILOGE("properties or propertyMask is less than 0");
101         return false;
102     }
103     return (static_cast<unsigned int>(properties) & static_cast<unsigned int>(propertyMask)) != 0;
104 }
ConvertGattPropertiesToJs(napi_env env,int properties)105 napi_value ConvertGattPropertiesToJs(napi_env env, int properties)
106 {
107     napi_value object;
108     napi_create_object(env, &object);
109 
110     napi_value value;
111     napi_get_boolean(env, HasProperty(properties, GattCharacteristic::WRITE), &value);
112     napi_set_named_property(env, object, "write", value);
113 
114     napi_get_boolean(env, HasProperty(properties, GattCharacteristic::WRITE_WITHOUT_RESPONSE), &value);
115     napi_set_named_property(env, object, "writeNoResponse", value);
116 
117     napi_get_boolean(env, HasProperty(properties, GattCharacteristic::READ), &value);
118     napi_set_named_property(env, object, "read", value);
119 
120     napi_get_boolean(env, HasProperty(properties, GattCharacteristic::NOTIFY), &value);
121     napi_set_named_property(env, object, "notify", value);
122 
123     napi_get_boolean(env, HasProperty(properties, GattCharacteristic::INDICATE), &value);
124     napi_set_named_property(env, object, "indicate", value);
125     return object;
126 }
127 
ConvertBLECharacteristicToJS(napi_env env,napi_value result,GattCharacteristic & characteristic)128 void ConvertBLECharacteristicToJS(napi_env env, napi_value result, GattCharacteristic& characteristic)
129 {
130     napi_value characteristicUuid;
131     HILOGI("uuid: %{public}s", characteristic.GetUuid().ToString().c_str());
132     napi_create_string_utf8(env, characteristic.GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH, &characteristicUuid);
133     napi_set_named_property(env, result, "characteristicUuid", characteristicUuid);
134 
135     if (characteristic.GetService() != nullptr) {
136         napi_value serviceUuid;
137         napi_create_string_utf8(env, characteristic.GetService()->GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH,
138             &serviceUuid);
139         napi_set_named_property(env, result, "serviceUuid", serviceUuid);
140     }
141 
142     size_t valueSize = 0;
143     uint8_t* valueData = characteristic.GetValue(&valueSize).get();
144     {
145         napi_value value = nullptr;
146         uint8_t* bufferData = nullptr;
147         napi_create_arraybuffer(env, valueSize, (void**)&bufferData, &value);
148         if (valueSize > 0 && memcpy_s(bufferData, valueSize, valueData, valueSize) != EOK) {
149             HILOGE("memcpy_s failed");
150             return;
151         }
152         napi_set_named_property(env, result, "characteristicValue", value);
153     }
154 
155     napi_value propertiesValue = ConvertGattPropertiesToJs(env, characteristic.GetProperties());
156     napi_set_named_property(env, result, "properties", propertiesValue);
157 
158     napi_value descriptors;
159     napi_create_array(env, &descriptors);
160     ConvertBLEDescriptorVectorToJS(env, descriptors, characteristic.GetDescriptors());
161     napi_set_named_property(env, result, "descriptors", descriptors);
162 }
163 
ConvertBLEDescriptorVectorToJS(napi_env env,napi_value result,vector<GattDescriptor> & descriptors)164 void ConvertBLEDescriptorVectorToJS(napi_env env, napi_value result, vector<GattDescriptor>& descriptors)
165 {
166     HILOGI("size: %{public}zu", descriptors.size());
167     size_t idx = 0;
168 
169     if (descriptors.empty()) {
170         return;
171     }
172 
173     for (auto& descriptor : descriptors) {
174         napi_value obj = nullptr;
175         napi_create_object(env, &obj);
176         ConvertBLEDescriptorToJS(env, obj, descriptor);
177         napi_set_element(env, result, idx, obj);
178         idx++;
179     }
180 }
181 
ConvertBLEDescriptorToJS(napi_env env,napi_value result,GattDescriptor & descriptor)182 void ConvertBLEDescriptorToJS(napi_env env, napi_value result, GattDescriptor& descriptor)
183 {
184     HILOGI("uuid: %{public}s", descriptor.GetUuid().ToString().c_str());
185 
186     napi_value descriptorUuid;
187     napi_create_string_utf8(env, descriptor.GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH, &descriptorUuid);
188     napi_set_named_property(env, result, "descriptorUuid", descriptorUuid);
189 
190     if (descriptor.GetCharacteristic() != nullptr) {
191         napi_value characteristicUuid;
192         napi_create_string_utf8(env, descriptor.GetCharacteristic()->GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH,
193             &characteristicUuid);
194         napi_set_named_property(env, result, "characteristicUuid", characteristicUuid);
195 
196         if (descriptor.GetCharacteristic()->GetService() != nullptr) {
197             napi_value serviceUuid;
198             napi_create_string_utf8(env, descriptor.GetCharacteristic()->GetService()->GetUuid().ToString().c_str(),
199                 NAPI_AUTO_LENGTH, &serviceUuid);
200             napi_set_named_property(env, result, "serviceUuid", serviceUuid);
201         }
202     }
203 
204     napi_value value;
205     size_t valueSize;
206     uint8_t* valueData = descriptor.GetValue(&valueSize).get();
207     uint8_t* bufferData = nullptr;
208     napi_create_arraybuffer(env, valueSize, (void**)&bufferData, &value);
209     if (memcpy_s(bufferData, valueSize, valueData, valueSize) != EOK) {
210         HILOGE("memcpy_s error");
211     }
212     napi_set_named_property(env, result, "descriptorValue", value);
213 }
214 
ConvertCharacteristicReadReqToJS(napi_env env,napi_value result,const std::string & device,const GattCharacteristic & characteristic,int requestId)215 void ConvertCharacteristicReadReqToJS(napi_env env, napi_value result, const std::string &device,
216     const GattCharacteristic &characteristic, int requestId)
217 {
218     napi_value deviceId;
219     napi_create_string_utf8(env, device.c_str(), NAPI_AUTO_LENGTH, &deviceId);
220     napi_set_named_property(env, result, "deviceId", deviceId);
221 
222     napi_value transId;
223     napi_create_int32(env, requestId, &transId);
224     napi_set_named_property(env, result, "transId", transId);
225 
226     napi_value offset;
227     napi_create_int32(env, 0, &offset);
228     napi_set_named_property(env, result, "offset", offset);
229     HILOGI("offset is %{public}d", 0);
230 
231     napi_value characteristicUuid;
232     napi_create_string_utf8(env, characteristic.GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH, &characteristicUuid);
233     napi_set_named_property(env, result, "characteristicUuid", characteristicUuid);
234     HILOGI("characteristicUuid is %{public}s", characteristic.GetUuid().ToString().c_str());
235 
236     if (characteristic.GetService() != nullptr) {
237         napi_value serviceUuid;
238         napi_create_string_utf8(env, characteristic.GetService()->GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH,
239             &serviceUuid);
240         napi_set_named_property(env, result, "serviceUuid", serviceUuid);
241     }
242 }
243 
ConvertDescriptorReadReqToJS(napi_env env,napi_value result,const std::string & device,const GattDescriptor & descriptor,int requestId)244 void ConvertDescriptorReadReqToJS(napi_env env, napi_value result, const std::string &device,
245     const GattDescriptor& descriptor, int requestId)
246 {
247     napi_value deviceId;
248     napi_create_string_utf8(env, device.c_str(), NAPI_AUTO_LENGTH, &deviceId);
249     napi_set_named_property(env, result, "deviceId", deviceId);
250 
251     napi_value transId;
252     napi_create_int32(env, requestId, &transId);
253     napi_set_named_property(env, result, "transId", transId);
254 
255     napi_value offset;
256     napi_create_int32(env, 0, &offset);
257     napi_set_named_property(env, result, "offset", offset);
258     HILOGI("offset is %{public}d", 0);
259 
260     napi_value descriptorUuid;
261     napi_create_string_utf8(env, descriptor.GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH, &descriptorUuid);
262     napi_set_named_property(env, result, "descriptorUuid", descriptorUuid);
263     HILOGI("descriptorUuid is %{public}s", descriptor.GetUuid().ToString().c_str());
264 
265     if (descriptor.GetCharacteristic() != nullptr) {
266     napi_value characteristicUuid;
267     napi_create_string_utf8(env, descriptor.GetCharacteristic()->GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH,
268         &characteristicUuid);
269     napi_set_named_property(env, result, "characteristicUuid", characteristicUuid);
270 
271         if (descriptor.GetCharacteristic()->GetService() != nullptr) {
272             napi_value serviceUuid;
273             napi_create_string_utf8(env, descriptor.GetCharacteristic()->GetService()->GetUuid().ToString().c_str(),
274                 NAPI_AUTO_LENGTH, &serviceUuid);
275             napi_set_named_property(env, result, "serviceUuid", serviceUuid);
276         }
277     }
278 }
279 
ConvertCharacteristicWriteReqToJS(napi_env env,napi_value result,const std::string & device,const GattCharacteristic & characteristic,int requestId)280 void ConvertCharacteristicWriteReqToJS(napi_env env, napi_value result, const std::string &device,
281     const GattCharacteristic& characteristic, int requestId)
282 {
283     napi_value deviceId;
284     napi_create_string_utf8(env, device.c_str(), NAPI_AUTO_LENGTH, &deviceId);
285     napi_set_named_property(env, result, "deviceId", deviceId);
286 
287     napi_value transId;
288     napi_create_int32(env, requestId, &transId);
289     napi_set_named_property(env, result, "transId", transId);
290 
291     napi_value offset;
292     napi_create_int32(env, 0, &offset);
293     napi_set_named_property(env, result, "offset", offset);
294     HILOGI("offset is %{public}d", 0);
295 
296     napi_value isPrepared;
297     napi_get_boolean(env, false, &isPrepared);
298     napi_set_named_property(env, result, "isPrepared", isPrepared);
299 
300     napi_value needRsp;
301     napi_get_boolean(env, characteristic.GetWriteType() == GattCharacteristic::WriteType::DEFAULT, &needRsp);
302     napi_set_named_property(env, result, "needRsp", needRsp);
303 
304     napi_value value;
305     size_t valueSize;
306     uint8_t* valueData = characteristic.GetValue(&valueSize).get();
307     uint8_t* bufferData = nullptr;
308     napi_create_arraybuffer(env, valueSize, (void**)&bufferData, &value);
309     if (memcpy_s(bufferData, valueSize, valueData, valueSize) != EOK) {
310         HILOGE("memcpy_s error");
311     }
312     napi_set_named_property(env, result, "value", value);
313 
314     napi_value characteristicUuid;
315     napi_create_string_utf8(env, characteristic.GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH, &characteristicUuid);
316     napi_set_named_property(env, result, "characteristicUuid", characteristicUuid);
317     HILOGI("characteristicUuid is %{public}s", characteristic.GetUuid().ToString().c_str());
318 
319     if (characteristic.GetService() != nullptr) {
320         napi_value serviceUuid;
321         napi_create_string_utf8(env, characteristic.GetService()->GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH,
322             &serviceUuid);
323         napi_set_named_property(env, result, "serviceUuid", serviceUuid);
324     }
325 }
326 
ConvertDescriptorWriteReqToJS(napi_env env,napi_value result,const std::string & device,const GattDescriptor & descriptor,int requestId)327 void ConvertDescriptorWriteReqToJS(napi_env env, napi_value result, const std::string &device,
328     const GattDescriptor &descriptor, int requestId)
329 {
330     napi_value deviceId;
331     napi_create_string_utf8(env, device.c_str(), NAPI_AUTO_LENGTH, &deviceId);
332     napi_set_named_property(env, result, "deviceId", deviceId);
333 
334     napi_value transId;
335     napi_create_int32(env, requestId, &transId);
336     napi_set_named_property(env, result, "transId", transId);
337 
338     napi_value offset;
339     napi_create_int32(env, 0, &offset);
340     napi_set_named_property(env, result, "offset", offset);
341     HILOGI("offset is %{public}d", 0);
342 
343     napi_value isPrepared;
344     napi_get_boolean(env, false, &isPrepared);
345     napi_set_named_property(env, result, "isPrepared", isPrepared);
346 
347     napi_value needRsp;
348     napi_get_boolean(env, true, &needRsp);
349     napi_set_named_property(env, result, "needRsp", needRsp);
350 
351     napi_value value;
352     size_t valueSize;
353     uint8_t* valueData = descriptor.GetValue(&valueSize).get();
354     uint8_t* bufferData = nullptr;
355     napi_create_arraybuffer(env, valueSize, (void**)&bufferData, &value);
356     if (memcpy_s(bufferData, valueSize, valueData, valueSize) != EOK) {
357         HILOGE("memcpy_s error");
358     }
359     napi_set_named_property(env, result, "value", value);
360 
361     napi_value descriptorUuid;
362     napi_create_string_utf8(env, descriptor.GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH, &descriptorUuid);
363     napi_set_named_property(env, result, "descriptorUuid", descriptorUuid);
364     HILOGI("descriptorUuid is %{public}s", descriptor.GetUuid().ToString().c_str());
365 
366     if (descriptor.GetCharacteristic() != nullptr) {
367         napi_value characteristicUuid;
368         napi_create_string_utf8(env, descriptor.GetCharacteristic()->GetUuid().ToString().c_str(), NAPI_AUTO_LENGTH,
369             &characteristicUuid);
370         napi_set_named_property(env, result, "characteristicUuid", characteristicUuid);
371 
372         if (descriptor.GetCharacteristic()->GetService() != nullptr) {
373             napi_value serviceUuid;
374             napi_create_string_utf8(env, descriptor.GetCharacteristic()->GetService()->GetUuid().ToString().c_str(),
375                 NAPI_AUTO_LENGTH, &serviceUuid);
376             napi_set_named_property(env, result, "serviceUuid", serviceUuid);
377         }
378     }
379 }
380 
SetGattClientDeviceId(const std::string & deviceId)381 void SetGattClientDeviceId(const std::string &deviceId)
382 {
383     deviceAddr = deviceId;
384 }
385 
GetGattClientDeviceId()386 std::string GetGattClientDeviceId()
387 {
388     return deviceAddr;
389 }
390 
ToNapiValue(napi_env env) const391 napi_value NapiNativeBleCharacteristic::ToNapiValue(napi_env env) const
392 {
393     napi_value object;
394     napi_create_object(env, &object);
395     ConvertBLECharacteristicToJS(env, object, const_cast<GattCharacteristic &>(character_));
396     return object;
397 }
398 
ToNapiValue(napi_env env) const399 napi_value NapiNativeBleDescriptor::ToNapiValue(napi_env env) const
400 {
401     napi_value object;
402     napi_create_object(env, &object);
403     ConvertBLEDescriptorToJS(env, object, const_cast<GattDescriptor &>(descriptor_));
404     return object;
405 }
406 
ToNapiValue(napi_env env) const407 napi_value NapiNativeGattServiceArray::ToNapiValue(napi_env env) const
408 {
409     napi_value object;
410     napi_create_array(env, &object);
411     ConvertGattServiceVectorToJS(env, object, const_cast<vector<GattService> &>(gattServices_));
412     return object;
413 }
414 
ToNapiValue(napi_env env) const415 napi_value NapiNativeAdvertisingStateInfo::ToNapiValue(napi_env env) const
416 {
417     napi_value object;
418     napi_create_object(env, &object);
419 
420     napi_value value;
421     napi_create_int32(env, advHandle_, &value);
422     napi_set_named_property(env, object, "advertisingId", value);
423 
424     napi_create_int32(env, advState_, &value);
425     napi_set_named_property(env, object, "state", value);
426     return object;
427 }
428 
ToNapiValue(napi_env env) const429 napi_value NapiNativeGattsCharacterReadRequest::ToNapiValue(napi_env env) const
430 {
431     napi_value result = nullptr;
432     napi_create_object(env, &result);
433 
434     ConvertCharacteristicReadReqToJS(env, result, deviceAddr_, character_, transId_);
435     return result;
436 }
437 
ToNapiValue(napi_env env) const438 napi_value NapiNativeGattsCharacterWriteRequest::ToNapiValue(napi_env env) const
439 {
440     napi_value result = nullptr;
441     napi_create_object(env, &result);
442 
443     ConvertCharacteristicWriteReqToJS(env, result, deviceAddr_, character_, transId_);
444     return result;
445 }
446 
ToNapiValue(napi_env env) const447 napi_value NapiNativeGattsDescriptorWriteRequest::ToNapiValue(napi_env env) const
448 {
449     napi_value result = nullptr;
450     napi_create_object(env, &result);
451 
452     ConvertDescriptorWriteReqToJS(env, result, deviceAddr_, descriptor_, transId_);
453     return result;
454 }
455 
ToNapiValue(napi_env env) const456 napi_value NapiNativeGattsDescriptorReadRequest::ToNapiValue(napi_env env) const
457 {
458     napi_value result = nullptr;
459     napi_create_object(env, &result);
460 
461     ConvertDescriptorReadReqToJS(env, result, deviceAddr_, descriptor_, transId_);
462     return result;
463 }
464 }  // namespace Bluetooth
465 }  // namespace OHOS
466