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