• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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