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_gatt_server"
17 #endif
18
19 #include "napi_bluetooth_gatt_server.h"
20 #include "bluetooth_gatt_service.h"
21 #include "bluetooth_host.h"
22 #include "bluetooth_log.h"
23 #include "bluetooth_utils.h"
24 #include "napi_bluetooth_ble.h"
25 #include "napi_bluetooth_ble_utils.h"
26 #include "napi_bluetooth_error.h"
27 #include "napi_bluetooth_utils.h"
28 #include "napi_event_subscribe_module.h"
29 #include "../parser/napi_parser_utils.h"
30
31 namespace OHOS {
32 namespace Bluetooth {
33 using namespace std;
34
35 std::vector<std::string> NapiGattServer::deviceList_;
36 std::mutex NapiGattServer::deviceListMutex_;
37 thread_local napi_ref NapiGattServer::consRef_ = nullptr;
38
CreateGattServer(napi_env env,napi_callback_info info)39 napi_value NapiGattServer::CreateGattServer(napi_env env, napi_callback_info info)
40 {
41 HILOGI("enter");
42 napi_value result;
43 napi_value constructor = nullptr;
44 napi_get_reference_value(env, consRef_, &constructor);
45 napi_new_instance(env, constructor, 0, nullptr, &result);
46
47 return result;
48 }
49
DefineGattServerJSClass(napi_env env)50 void NapiGattServer::DefineGattServerJSClass(napi_env env)
51 {
52 napi_property_descriptor gattserverDesc[] = {
53 #ifdef BLUETOOTH_API_SINCE_10
54 DECLARE_NAPI_FUNCTION("notifyCharacteristicChanged", NotifyCharacteristicChangedEx),
55 #else
56 DECLARE_NAPI_FUNCTION("startAdvertising", StartAdvertising),
57 DECLARE_NAPI_FUNCTION("stopAdvertising", StopAdvertising),
58 DECLARE_NAPI_FUNCTION("notifyCharacteristicChanged", NotifyCharacteristicChanged),
59 #endif
60 DECLARE_NAPI_FUNCTION("addService", AddService),
61 DECLARE_NAPI_FUNCTION("removeService", RemoveGattService),
62 DECLARE_NAPI_FUNCTION("close", Close),
63 DECLARE_NAPI_FUNCTION("sendResponse", SendResponse),
64 DECLARE_NAPI_FUNCTION("on", On),
65 DECLARE_NAPI_FUNCTION("off", Off),
66 };
67
68 napi_value constructor = nullptr;
69 napi_define_class(env, "GattServer", NAPI_AUTO_LENGTH, GattServerConstructor, nullptr,
70 sizeof(gattserverDesc) / sizeof(gattserverDesc[0]), gattserverDesc, &constructor);
71 napi_create_reference(env, constructor, 1, &consRef_);
72 }
73
GattServerConstructor(napi_env env,napi_callback_info info)74 napi_value NapiGattServer::GattServerConstructor(napi_env env, napi_callback_info info)
75 {
76 napi_value thisVar = nullptr;
77 napi_get_cb_info(env, info, nullptr, nullptr, &thisVar, nullptr);
78 NapiGattServer* gattServer = new NapiGattServer();
79
80 auto status = napi_wrap(
81 env, thisVar, gattServer,
82 [](napi_env env, void* data, void* hint) {
83 NapiGattServer* server = static_cast<NapiGattServer*>(data);
84 if (server) {
85 delete server;
86 server = nullptr;
87 }
88 },
89 nullptr,
90 nullptr);
91
92 if (status != napi_ok) {
93 HILOGE("napi_wrap failed");
94 delete gattServer;
95 gattServer = nullptr;
96 }
97
98 return thisVar;
99 }
100
NapiGetGattServer(napi_env env,napi_value thisVar)101 static NapiGattServer *NapiGetGattServer(napi_env env, napi_value thisVar)
102 {
103 NapiGattServer *gattServer = nullptr;
104 auto status = napi_unwrap(env, thisVar, (void **)&gattServer);
105 if (status != napi_ok) {
106 return nullptr;
107 }
108 return gattServer;
109 }
110
NapiGetGattServer(napi_env env,napi_callback_info info)111 static NapiGattServer *NapiGetGattServer(napi_env env, napi_callback_info info)
112 {
113 size_t argc = 0;
114 napi_value thisVar = nullptr;
115 if (napi_get_cb_info(env, info, &argc, nullptr, &thisVar, nullptr) != napi_ok) {
116 return nullptr;
117 }
118 return NapiGetGattServer(env, thisVar);
119 }
120
On(napi_env env,napi_callback_info info)121 napi_value NapiGattServer::On(napi_env env, napi_callback_info info)
122 {
123 NapiGattServer *napiGattServer = NapiGetGattServer(env, info);
124 if (napiGattServer && napiGattServer->GetCallback()) {
125 auto status = napiGattServer->GetCallback()->eventSubscribe_.Register(env, info);
126 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
127 }
128 return NapiGetUndefinedRet(env);
129 }
130
Off(napi_env env,napi_callback_info info)131 napi_value NapiGattServer::Off(napi_env env, napi_callback_info info)
132 {
133 NapiGattServer *napiGattServer = NapiGetGattServer(env, info);
134 if (napiGattServer && napiGattServer->GetCallback()) {
135 auto status = napiGattServer->GetCallback()->eventSubscribe_.Deregister(env, info);
136 NAPI_BT_ASSERT_RETURN_UNDEF(env, status == napi_ok, BT_ERR_INVALID_PARAM);
137 }
138 return NapiGetUndefinedRet(env);
139 }
140
CheckGattsAddService(napi_env env,napi_callback_info info,std::shared_ptr<GattServer> & outServer,std::unique_ptr<GattService> & outService)141 static napi_status CheckGattsAddService(napi_env env, napi_callback_info info, std::shared_ptr<GattServer> &outServer,
142 std::unique_ptr<GattService> &outService)
143 {
144 size_t argc = ARGS_SIZE_ONE;
145 napi_value argv[ARGS_SIZE_ONE] = {nullptr};
146 napi_value thisVar = nullptr;
147 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
148 NAPI_BT_RETURN_IF(argc != ARGS_SIZE_ONE, "Requires 1 arguments.", napi_invalid_arg);
149
150 // std::unique_ptr<GattService> service {nullptr};
151 NapiGattService napiGattService;
152 NAPI_BT_CALL_RETURN(NapiParseGattService(env, argv[PARAM0], napiGattService));
153
154 NapiGattServer *gattServer = NapiGetGattServer(env, thisVar);
155 NAPI_BT_RETURN_IF(gattServer == nullptr, "gattServer is nullptr.", napi_invalid_arg);
156 outServer = gattServer->GetServer();
157
158 GattServiceType type = napiGattService.isPrimary ? GattServiceType::PRIMARY : GattServiceType::SECONDARY;
159 outService = std::make_unique<GattService>(napiGattService.serviceUuid, type);
160 for (const auto &napiCharacter : napiGattService.characteristics) {
161 int charPermissions = napiCharacter.permissions;
162 int charProperties = napiCharacter.properties;
163 GattCharacteristic character(napiCharacter.characteristicUuid, charPermissions, charProperties);
164 character.SetValue(napiCharacter.characteristicValue.data(), napiCharacter.characteristicValue.size());
165
166 for (const auto &napiDescriptor : napiCharacter.descriptors) {
167 GattDescriptor descriptor(napiDescriptor.descriptorUuid, napiDescriptor.permissions);
168 descriptor.SetValue(napiDescriptor.descriptorValue.data(), napiDescriptor.descriptorValue.size());
169 character.AddDescriptor(descriptor);
170 }
171 outService->AddCharacteristic(character);
172 }
173
174 return napi_ok;
175 }
176
AddService(napi_env env,napi_callback_info info)177 napi_value NapiGattServer::AddService(napi_env env, napi_callback_info info)
178 {
179 HILOGI("enter");
180 std::shared_ptr<GattServer> server {nullptr};
181 std::unique_ptr<GattService> gattService {nullptr};
182 auto status = CheckGattsAddService(env, info, server, gattService);
183 NAPI_BT_ASSERT_RETURN_FALSE(env, (status == napi_ok && server != nullptr), BT_ERR_INVALID_PARAM);
184
185 int ret = server->AddService(*gattService);
186 NAPI_BT_ASSERT_RETURN_FALSE(env, ret == BT_NO_ERROR, ret);
187 return NapiGetBooleanTrue(env);
188 }
189
CheckGattsClose(napi_env env,napi_callback_info info,std::shared_ptr<GattServer> & outServer)190 static napi_status CheckGattsClose(napi_env env, napi_callback_info info, std::shared_ptr<GattServer> &outServer)
191 {
192 size_t argc = 0;
193 napi_value thisVar = nullptr;
194 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, nullptr, &thisVar, NULL));
195 NAPI_BT_RETURN_IF(argc > 0, "no needed arguments.", napi_invalid_arg);
196 NapiGattServer *gattServer = NapiGetGattServer(env, thisVar);
197 NAPI_BT_RETURN_IF(gattServer == nullptr, "gattServer is nullptr.", napi_invalid_arg);
198
199 outServer = gattServer->GetServer();
200 return napi_ok;
201 }
202
Close(napi_env env,napi_callback_info info)203 napi_value NapiGattServer::Close(napi_env env, napi_callback_info info)
204 {
205 HILOGI("enter");
206 std::shared_ptr<GattServer> server {nullptr};
207 auto status = CheckGattsClose(env, info, server);
208 NAPI_BT_ASSERT_RETURN_UNDEF(env, (status == napi_ok && server != nullptr), BT_ERR_INVALID_PARAM);
209
210 int ret = server->Close();
211 NAPI_BT_ASSERT_RETURN_UNDEF(env, ret == BT_NO_ERROR, ret);
212 return NapiGetUndefinedRet(env);
213 }
214
CheckGattsRemoveService(napi_env env,napi_callback_info info,std::shared_ptr<GattServer> & outServer,UUID & outUuid)215 static napi_status CheckGattsRemoveService(napi_env env, napi_callback_info info,
216 std::shared_ptr<GattServer> &outServer, UUID &outUuid)
217 {
218 size_t argc = 1;
219 napi_value argv[1] = {nullptr};
220 napi_value thisVar = nullptr;
221 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
222 NAPI_BT_RETURN_IF(argc != 1, "Requires 1 arguments.", napi_invalid_arg);
223
224 std::string uuid {};
225 NAPI_BT_CALL_RETURN(NapiParseUuid(env, argv[0], uuid));
226
227 NapiGattServer *gattServer = NapiGetGattServer(env, thisVar);
228 NAPI_BT_RETURN_IF(gattServer == nullptr, "gattServer is nullptr.", napi_invalid_arg);
229 outServer = gattServer->GetServer();
230 outUuid = UUID::FromString(uuid);
231 return napi_ok;
232 }
233
RemoveGattService(napi_env env,napi_callback_info info)234 napi_value NapiGattServer::RemoveGattService(napi_env env, napi_callback_info info)
235 {
236 HILOGI("enter");
237 std::shared_ptr<GattServer> server {nullptr};
238 UUID serviceUuid;
239 auto status = CheckGattsRemoveService(env, info, server, serviceUuid);
240 NAPI_BT_ASSERT_RETURN_FALSE(env, (status == napi_ok && server != nullptr), BT_ERR_INVALID_PARAM);
241
242 int ret = BT_ERR_INTERNAL_ERROR;
243 auto primaryService = server->GetService(serviceUuid, true);
244 if (primaryService.has_value()) {
245 ret = server->RemoveGattService(*primaryService);
246 NAPI_BT_ASSERT_RETURN_FALSE(env, ret == BT_NO_ERROR, ret);
247 }
248 auto secondService = server->GetService(serviceUuid, false);
249 if (secondService.has_value()) {
250 ret = server->RemoveGattService(*secondService);
251 NAPI_BT_ASSERT_RETURN_FALSE(env, ret == BT_NO_ERROR, ret);
252 }
253 NAPI_BT_ASSERT_RETURN_FALSE(env, (primaryService.has_value() || secondService.has_value()), BT_ERR_INVALID_PARAM);
254 return NapiGetBooleanRet(env, ret == BT_NO_ERROR);
255 }
256
CheckGattsSendRsp(napi_env env,napi_callback_info info,std::shared_ptr<GattServer> & outServer,NapiGattsServerResponse & outRsp)257 static napi_status CheckGattsSendRsp(napi_env env, napi_callback_info info, std::shared_ptr<GattServer> &outServer,
258 NapiGattsServerResponse &outRsp)
259 {
260 size_t argc = 1;
261 napi_value argv[1] = {nullptr};
262 napi_value thisVar = nullptr;
263 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
264 NAPI_BT_RETURN_IF(argc != 1, "Requires 1 arguments.", napi_invalid_arg);
265
266 NapiGattsServerResponse rsp;
267 NAPI_BT_CALL_RETURN(NapiParseGattsServerResponse(env, argv[0], rsp));
268
269 NapiGattServer *gattServer = NapiGetGattServer(env, thisVar);
270 NAPI_BT_RETURN_IF(gattServer == nullptr, "gattServer is nullptr.", napi_invalid_arg);
271 outServer = gattServer->GetServer();
272 outRsp = std::move(rsp);
273 return napi_ok;
274 }
275
SendResponse(napi_env env,napi_callback_info info)276 napi_value NapiGattServer::SendResponse(napi_env env, napi_callback_info info)
277 {
278 HILOGI("enter");
279 std::shared_ptr<GattServer> server {nullptr};
280 NapiGattsServerResponse rsp;
281 auto status = CheckGattsSendRsp(env, info, server, rsp);
282 NAPI_BT_ASSERT_RETURN_FALSE(env, (status == napi_ok && server != nullptr), BT_ERR_INVALID_PARAM);
283
284 BluetoothRemoteDevice remoteDevice(rsp.deviceId, BTTransport::ADAPTER_BLE);
285 HILOGI("Remote device address: %{public}s", GET_ENCRYPT_ADDR(remoteDevice));
286 int ret = server->SendResponse(remoteDevice, rsp.transId, rsp.status, rsp.offset, rsp.value.data(),
287 static_cast<int>(rsp.value.size()));
288 NAPI_BT_ASSERT_RETURN_FALSE(env, ret == BT_NO_ERROR, ret);
289 return NapiGetBooleanTrue(env);
290 }
291
GetGattCharacteristic(const std::shared_ptr<GattServer> & server,const UUID & serviceUuid,const UUID & characterUuid)292 static GattCharacteristic *GetGattCharacteristic(const std::shared_ptr<GattServer> &server, const UUID &serviceUuid,
293 const UUID &characterUuid)
294 {
295 auto service = server->GetService(serviceUuid, true);
296 if (!service.has_value()) {
297 service = server->GetService(serviceUuid, false);
298 }
299 if (!service.has_value()) {
300 HILOGE("not found service uuid: %{public}s", serviceUuid.ToString().c_str());
301 return nullptr;
302 }
303 GattCharacteristic *character = service.value().get().GetCharacteristic(characterUuid);
304 return character;
305 }
306
307 #ifdef BLUETOOTH_API_SINCE_10
CheckNotifyCharacteristicChangedEx(napi_env env,napi_callback_info info,NapiGattServer ** outServer,std::string & outDeviceId,NapiNotifyCharacteristic & outCharacter)308 static napi_status CheckNotifyCharacteristicChangedEx(napi_env env, napi_callback_info info,
309 NapiGattServer **outServer, std::string &outDeviceId, NapiNotifyCharacteristic &outCharacter)
310 {
311 size_t argc = ARGS_SIZE_THREE;
312 napi_value argv[ARGS_SIZE_THREE] = {0};
313 napi_value thisVar = nullptr;
314 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
315 NAPI_BT_RETURN_IF(argc != ARGS_SIZE_TWO && argc != ARGS_SIZE_THREE, "Requires 2 or 3 arguments.", napi_invalid_arg);
316
317 std::string deviceId {};
318 NapiNotifyCharacteristic character;
319 NAPI_BT_CALL_RETURN(NapiParseBdAddr(env, argv[PARAM0], deviceId));
320 NAPI_BT_CALL_RETURN(NapiParseNotifyCharacteristic(env, argv[PARAM1], character));
321
322 NapiGattServer *gattServer = NapiGetGattServer(env, thisVar);
323 NAPI_BT_RETURN_IF(gattServer == nullptr || outServer ==nullptr, "gattServer is nullptr.", napi_invalid_arg);
324 *outServer = gattServer;
325 outDeviceId = std::move(deviceId);
326 outCharacter = std::move(character);
327 return napi_ok;
328 }
329
NotifyCharacteristicChangedEx(napi_env env,napi_callback_info info)330 napi_value NapiGattServer::NotifyCharacteristicChangedEx(napi_env env, napi_callback_info info)
331 {
332 HILOGI("enter");
333 NapiGattServer* napiServer = nullptr;
334 std::string deviceId {};
335 NapiNotifyCharacteristic notifyCharacter;
336 auto status = CheckNotifyCharacteristicChangedEx(env, info, &napiServer, deviceId, notifyCharacter);
337 NAPI_BT_ASSERT_RETURN_FALSE(env, (status == napi_ok && napiServer && napiServer->GetServer()),
338 BT_ERR_INVALID_PARAM);
339
340 auto func = [server = napiServer->GetServer(), notifyCharacter, deviceId]() {
341 int ret = BT_ERR_INTERNAL_ERROR;
342 auto character = GetGattCharacteristic(server, notifyCharacter.serviceUuid, notifyCharacter.characterUuid);
343 if (character == nullptr) {
344 HILOGI("character is null!");
345 return NapiAsyncWorkRet(ret);
346 }
347 character->SetValue(notifyCharacter.characterValue.data(), notifyCharacter.characterValue.size());
348 BluetoothRemoteDevice remoteDevice(deviceId, BTTransport::ADAPTER_BLE);
349 ret = server->NotifyCharacteristicChanged(remoteDevice, *character, notifyCharacter.confirm);
350 return NapiAsyncWorkRet(ret);
351 };
352 auto asyncWork = NapiAsyncWorkFactory::CreateAsyncWork(env, info, func, ASYNC_WORK_NEED_CALLBACK);
353 NAPI_BT_ASSERT_RETURN_UNDEF(env, asyncWork, BT_ERR_INTERNAL_ERROR);
354
355 bool success = napiServer->GetCallback()->asyncWorkMap_.TryPush(NapiAsyncType::GATT_SERVER_NOTIFY_CHARACTERISTIC,
356 asyncWork);
357 NAPI_BT_ASSERT_RETURN_UNDEF(env, success, BT_ERR_INTERNAL_ERROR);
358
359 asyncWork->Run();
360 return asyncWork->GetRet();
361 }
362 #else
CheckGattsNotify(napi_env env,napi_callback_info info,std::shared_ptr<GattServer> & outServer,std::string & outDeviceId,NapiNotifyCharacteristic & outCharacter)363 static napi_status CheckGattsNotify(napi_env env, napi_callback_info info, std::shared_ptr<GattServer> &outServer,
364 std::string &outDeviceId, NapiNotifyCharacteristic &outCharacter)
365 {
366 size_t argc = ARGS_SIZE_TWO;
367 napi_value argv[ARGS_SIZE_TWO] = {nullptr};
368 napi_value thisVar = nullptr;
369 NAPI_BT_CALL_RETURN(napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
370 NAPI_BT_RETURN_IF(argc != ARGS_SIZE_TWO, "Requires 2 arguments.", napi_invalid_arg);
371
372 std::string deviceId {};
373 NapiNotifyCharacteristic character;
374 NAPI_BT_CALL_RETURN(NapiParseBdAddr(env, argv[PARAM0], deviceId));
375 NAPI_BT_CALL_RETURN(NapiParseNotifyCharacteristic(env, argv[PARAM1], character));
376
377 NapiGattServer *gattServer = NapiGetGattServer(env, thisVar);
378 NAPI_BT_RETURN_IF(gattServer == nullptr, "gattServer is nullptr.", napi_invalid_arg);
379 outServer = gattServer->GetServer();
380 outDeviceId = std::move(deviceId);
381 outCharacter = std::move(character);
382 return napi_ok;
383 }
384
NotifyCharacteristicChanged(napi_env env,napi_callback_info info)385 napi_value NapiGattServer::NotifyCharacteristicChanged(napi_env env, napi_callback_info info)
386 {
387 HILOGI("enter");
388 std::shared_ptr<GattServer> server {nullptr};
389 std::string deviceId {};
390 NapiNotifyCharacteristic notifyCharacter;
391 auto status = CheckGattsNotify(env, info, server, deviceId, notifyCharacter);
392 NAPI_BT_ASSERT_RETURN_FALSE(env, (status == napi_ok && server != nullptr), BT_ERR_INVALID_PARAM);
393
394 auto character = GetGattCharacteristic(server, notifyCharacter.serviceUuid, notifyCharacter.characterUuid);
395 NAPI_BT_ASSERT_RETURN_FALSE(env, character != nullptr, BT_ERR_INVALID_PARAM);
396 character->SetValue(notifyCharacter.characterValue.data(), notifyCharacter.characterValue.size());
397
398 BluetoothRemoteDevice remoteDevice(deviceId, BTTransport::ADAPTER_BLE);
399 int ret = server->NotifyCharacteristicChanged(remoteDevice, *character, notifyCharacter.confirm);
400 NAPI_BT_ASSERT_RETURN_FALSE(env, ret == BT_NO_ERROR, ret);
401 return NapiGetBooleanTrue(env);
402 }
403 #endif
404 } // namespace Bluetooth
405 } // namespace OHOS
406