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