[Spec] Fixing conditional statement while generate rpm
[platform/core/api/webapi-plugins.git] / src / bluetooth / bluetooth_gatt_service.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 #include "bluetooth/bluetooth_gatt_service.h"
18
19 #include <sstream>
20
21 #include "common/extension.h"
22 #include "common/logger.h"
23 #include "common/platform_result.h"
24 #include "common/task-queue.h"
25 #include "common/tools.h"
26
27 #include "bluetooth/bluetooth_instance.h"
28 #include "bluetooth/bluetooth_privilege.h"
29 #include "bluetooth/bluetooth_util.h"
30
31 namespace extension {
32 namespace bluetooth {
33
34 using common::PlatformResult;
35 using common::ErrorCode;
36 using common::TaskQueue;
37 using namespace common::tools;
38
39 namespace {
40 const std::string kUuid = "uuid";
41 const std::string kHandle = "handle";
42 const std::string kAddress = "address";
43
44 const std::string kDescriptors = "descriptors";
45 const std::string kBroadcast = "isBroadcast";
46 const std::string kExtendedProperties = "hasExtendedProperties";
47 const std::string kNotify = "isNotify";
48 const std::string kIndication = "isIndication";
49 const std::string kReadable = "isReadable";
50 const std::string kSignedWrite = "isSignedWrite";
51 const std::string kWritable = "isWritable";
52 const std::string kWriteNoResponse = "isWriteNoResponse";
53
54 const std::string kOnValueChanged = "BluetoothGATTCharacteristicValueChangeListener";
55
56 bool IsProperty(int propertyBits, bt_gatt_property_e property) {
57   return (propertyBits & property) != 0;
58 }
59 }
60
61 BluetoothGATTService::BluetoothGATTService(BluetoothInstance& instance) : instance_(instance) {
62   ScopeLogger();
63 }
64
65 BluetoothGATTService::~BluetoothGATTService() {
66   ScopeLogger();
67
68   for (auto it : gatt_characteristic_) {
69     // unregister callback, ignore errors
70     bt_gatt_client_unset_characteristic_value_changed_cb(it);
71   }
72
73   for (auto it : gatt_clients_) {
74     LoggerD("destroying client for address: %s", it.first.c_str());
75     bt_gatt_client_destroy(it.second);
76   }
77 }
78
79 bool BluetoothGATTService::IsStillConnected(const std::string& address) {
80   auto it = gatt_clients_.find(address);
81   return gatt_clients_.end() != it;
82 }
83
84 bt_gatt_client_h BluetoothGATTService::GetGattClient(const std::string& address) {
85   ScopeLogger();
86
87   bt_gatt_client_h client = nullptr;
88
89   const auto it = gatt_clients_.find(address);
90
91   if (gatt_clients_.end() == it) {
92     int ret = bt_gatt_client_create(address.c_str(), &client);
93     if (BT_ERROR_NONE != ret) {
94       LoggerE("Failed to create GATT client, error: %d", ret);
95     } else {
96       gatt_clients_.insert(std::make_pair(address, client));
97     }
98   } else {
99     LoggerD("Client already created");
100     client = it->second;
101   }
102
103   return client;
104 }
105
106 // this method should be used to inform this object that some device was disconnected
107 void BluetoothGATTService::TryDestroyClient(const std::string& address) {
108   ScopeLogger();
109   auto it = gatt_clients_.find(address);
110   if (gatt_clients_.end() != it) {
111     LoggerD("destroying client for address: %s", it->first.c_str());
112     bt_gatt_client_destroy(it->second);
113     gatt_clients_.erase(it);
114   } else {
115     LoggerD("Client for address: %s does not exist, no need for deletion", address.c_str());
116   }
117 }
118
119 PlatformResult BluetoothGATTService::GetSpecifiedGATTService(const std::string& address,
120                                                              const std::string& uuid,
121                                                              picojson::object* result) {
122   ScopeLogger();
123
124   bt_gatt_client_h client = GetGattClient(address);
125
126   if (nullptr == client) {
127     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to create the GATT client's handle");
128   }
129
130   bt_gatt_h service = nullptr;
131   int ret = bt_gatt_client_get_service(client, uuid.c_str(), &service);
132   if (BT_ERROR_NONE != ret) {
133     LoggerE("bt_gatt_client_get_service() error: %d", ret);
134     switch (ret) {
135       case BT_ERROR_NO_DATA:
136         return LogAndCreateResult(
137             ErrorCode::NOT_FOUND_ERR, "Service not found",
138             ("bt_gatt_client_get_service error: %d (%s)", ret, get_error_message(ret)));
139
140       case BT_ERROR_INVALID_PARAMETER:
141         return LogAndCreateResult(
142             ErrorCode::NOT_FOUND_ERR, "Service UUID is invalid",
143             ("bt_gatt_client_get_service error: %d (%s)", ret, get_error_message(ret)));
144
145       default:
146         return LogAndCreateResult(
147             ErrorCode::UNKNOWN_ERR, "Failed to get a service's GATT handle",
148             ("bt_gatt_client_get_service error: %d (%s)", ret, get_error_message(ret)));
149     }
150   }
151
152   // report BluetoothGattService
153   result->insert(std::make_pair(kUuid, picojson::value(uuid)));
154   // handle is passed to upper layer because there is no need to delete it
155   result->insert(std::make_pair(kHandle, picojson::value((double)(long)service)));
156   // address is necessary to later check if device is still connected
157   result->insert(std::make_pair(kAddress, picojson::value(address)));
158   return PlatformResult(ErrorCode::NO_ERROR);
159 }
160
161 void BluetoothGATTService::GetServices(const picojson::value& args, picojson::object& out) {
162   ScopeLogger();
163
164   bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get("handle").get<double>());
165   const std::string& address = args.get("address").get<std::string>();
166
167   picojson::array array;
168   PlatformResult ret = GetServicesHelper(handle, address, &array);
169   if (ret.IsError()) {
170     LogAndReportError(ret, &out, ("Error while getting services"));
171   } else {
172     ReportSuccess(picojson::value(array), out);
173   }
174 }
175
176 PlatformResult BluetoothGATTService::GetServicesHelper(bt_gatt_h handle, const std::string& address,
177                                                        picojson::array* array) {
178   ScopeLogger();
179
180   if (!IsStillConnected(address)) {
181     return LogAndCreateResult(ErrorCode::INVALID_STATE_ERR, "Device is not connected",
182                               ("Device with address %s is no longer connected", address.c_str()));
183   }
184
185   int ret = bt_gatt_service_foreach_included_services(
186       handle,
187       [](int total, int index, bt_gatt_h gatt_handle, void* data) {
188         ScopeLogger(
189             "Entered into asynchronous function, argument in "
190             "bt_gatt_service_foreach_included_services");
191
192         picojson::value result = picojson::value(picojson::object());
193         picojson::object& result_obj = result.get<picojson::object>();
194
195         char* uuid = nullptr;
196
197         if (BT_ERROR_NONE == bt_gatt_get_uuid(gatt_handle, &uuid) && nullptr != uuid) {
198           result_obj.insert(std::make_pair(kUuid, picojson::value(uuid)));
199           free(uuid);
200         } else {
201           result_obj.insert(std::make_pair(kUuid, picojson::value("FFFF")));
202         }
203
204         // handle is passed to upper layer because there is no need of deletion
205         result_obj.insert(std::make_pair(kHandle, picojson::value((double)(long)gatt_handle)));
206         static_cast<picojson::array*>(data)->push_back(result);
207         return true;
208       },
209       array);
210   if (BT_ERROR_NONE != ret) {
211     LoggerE("Failed bt_gatt_service_foreach_included_services() (%d)", ret);
212     return util::GetBluetoothError(ret, "Failed to set a service's GATT callback");
213   }
214
215   return PlatformResult(ErrorCode::NO_ERROR);
216 }
217
218 void BluetoothGATTService::GetCharacteristics(const picojson::value& args, picojson::object& out) {
219   ScopeLogger();
220
221   bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get("handle").get<double>());
222   const std::string& uuid = args.get("uuid").get<std::string>();
223   const std::string& address = args.get("address").get<std::string>();
224
225   picojson::array array;
226   PlatformResult ret = GetCharacteristicsHelper(handle, address, uuid, &array);
227   if (ret.IsError()) {
228     LogAndReportError(ret, &out, ("Error while getting characteristics"));
229   } else {
230     ReportSuccess(picojson::value(array), out);
231   }
232 }
233
234 PlatformResult BluetoothGATTService::GetCharacteristicsHelper(bt_gatt_h handle,
235                                                               const std::string& address,
236                                                               const std::string& uuid,
237                                                               picojson::array* array) {
238   ScopeLogger();
239
240   if (!IsStillConnected(address)) {
241     return LogAndCreateResult(ErrorCode::INVALID_STATE_ERR, "Device is not connected",
242                               ("Device with address %s is no longer connected", address.c_str()));
243   }
244
245   struct Data {
246     picojson::array* array;
247     PlatformResult* platform_res;
248   };
249
250   PlatformResult platform_result = PlatformResult(ErrorCode::NO_ERROR);
251   Data user_data = {array, &platform_result};
252
253   int ret = bt_gatt_service_foreach_characteristics(
254       handle,
255       [](int total, int index, bt_gatt_h gatt_handle, void* data) {
256         ScopeLogger(
257             "Entered into asynchronous function, bt_gatt_service_foreach_characteristics's "
258             "argument");
259         Data* user_data = static_cast<Data*>(data);
260         picojson::array* array = user_data->array;
261         PlatformResult* platform_result = user_data->platform_res;
262
263         picojson::value result = picojson::value(picojson::object());
264         picojson::object& result_obj = result.get<picojson::object>();
265
266         // handle is passed to upper layer because there is no need of deletion
267         result_obj.insert(std::make_pair(kHandle, picojson::value((double)(long)gatt_handle)));
268
269         // descriptors
270         picojson::array& desc_array =
271             result_obj.insert(std::make_pair("descriptors", picojson::value(picojson::array())))
272                 .first->second.get<picojson::array>();
273         int ret = bt_gatt_characteristic_foreach_descriptors(
274             gatt_handle,
275             [](int total, int index, bt_gatt_h desc_handle, void* data) {
276               ScopeLogger();
277               picojson::array& desc_array = *(static_cast<picojson::array*>(data));
278
279               picojson::value desc = picojson::value(picojson::object());
280               picojson::object& desc_obj = desc.get<picojson::object>();
281
282               // handle is passed to upper layer because there is no need of deletion
283               desc_obj.insert(std::make_pair(kHandle, picojson::value((double)(long)desc_handle)));
284               desc_array.push_back(desc);
285               return true;
286             },
287             static_cast<void*>(&desc_array));
288         if (BT_ERROR_NONE != ret) {
289           *platform_result = util::GetBluetoothError(ret, "Failed to get descriptors");
290           LoggerE("Failed bt_gatt_characteristic_foreach_descriptors() (%d)", ret);
291           return false;
292         }
293
294         // other properties
295         int property_bits = 0;
296         int err = bt_gatt_characteristic_get_properties(gatt_handle, &property_bits);
297         if (BT_ERROR_NONE != err) {
298           LoggerE("Properties of characteristic couldn't be acquired");
299         }
300         result_obj.insert(std::make_pair(
301             kBroadcast, picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_BROADCAST))));
302         result_obj.insert(std::make_pair(
303             kReadable, picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_READ))));
304         result_obj.insert(std::make_pair(
305             kWriteNoResponse,
306             picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_WRITE_WITHOUT_RESPONSE))));
307         result_obj.insert(std::make_pair(
308             kWritable, picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_WRITE))));
309         result_obj.insert(std::make_pair(
310             kNotify, picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_NOTIFY))));
311         result_obj.insert(std::make_pair(
312             kIndication, picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_INDICATE))));
313         result_obj.insert(std::make_pair(
314             kSignedWrite, picojson::value(IsProperty(
315                               property_bits, BT_GATT_PROPERTY_AUTHENTICATED_SIGNED_WRITES))));
316         result_obj.insert(std::make_pair(
317             kExtendedProperties,
318             picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_EXTENDED_PROPERTIES))));
319
320         array->push_back(result);
321         return true;
322       },
323       static_cast<void*>(&user_data));
324   if (platform_result.IsError()) {
325     return platform_result;
326   }
327   if (BT_ERROR_NONE != ret) {
328     LoggerE("Failed (%d)", ret);
329     return util::GetBluetoothError(ret, "Failed while getting characteristic");
330   }
331
332   return PlatformResult(ErrorCode::NO_ERROR);
333 }
334
335 void BluetoothGATTService::ReadValue(const picojson::value& args, picojson::object& out) {
336   ScopeLogger();
337   CHECK_BACKWARD_COMPABILITY_PRIVILEGE_ACCESS(Privilege::kBluetooth, Privilege::kBluetoothAdmin,
338                                               &out);
339
340   const std::string& address = args.get("address").get<std::string>();
341   if (!IsStillConnected(address)) {
342     LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Device is not connected"), &out,
343                       ("Device with address %s is no longer connected", address.c_str()));
344     return;
345   }
346
347   const double callback_handle = util::GetAsyncCallbackHandle(args);
348   struct Data {
349     double callback_handle;
350     BluetoothGATTService* service;
351   };
352
353   Data* user_data = new Data{callback_handle, this};
354   bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get("handle").get<double>());
355
356   auto read_value = [](int result, bt_gatt_h handle, void* user_data) -> void {
357     ScopeLogger("Entered into asynchronous function, read_value");
358     Data* data = static_cast<Data*>(user_data);
359     double callback_handle = data->callback_handle;
360     BluetoothGATTService* service = data->service;
361     delete data;
362
363     PlatformResult plarform_res = PlatformResult(ErrorCode::NO_ERROR);
364
365     picojson::value byte_array = picojson::value(picojson::array());
366     picojson::array& byte_array_obj = byte_array.get<picojson::array>();
367
368     if (BT_ERROR_NONE != result) {
369       plarform_res = util::GetBluetoothError(result, "Error while reading value");
370     } else {
371       char* value = nullptr;
372       int length = 0;
373       int ret = bt_gatt_get_value(handle, &value, &length);
374       if (BT_ERROR_NONE != ret) {
375         plarform_res = util::GetBluetoothError(ret, "Error while getting value");
376       } else {
377         for (int i = 0; i < length; i++) {
378           byte_array_obj.push_back(picojson::value(std::to_string(value[i])));
379         }
380       }
381       if (value) {
382         free(value);
383         value = nullptr;
384       }
385     }
386
387     std::shared_ptr<picojson::value> response =
388         std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
389     if (plarform_res.IsSuccess()) {
390       ReportSuccess(byte_array, response->get<picojson::object>());
391     } else {
392       LogAndReportError(plarform_res, &response->get<picojson::object>());
393     }
394     TaskQueue::GetInstance().Async<picojson::value>(
395         [service, callback_handle](const std::shared_ptr<picojson::value>& response) {
396           service->instance_.SyncResponse(callback_handle, response);
397         },
398         response);
399   };
400   int ret = bt_gatt_client_read_value(handle, read_value, (void*)user_data);
401   if (BT_ERROR_NONE != ret) {
402     delete user_data;
403     user_data = nullptr;
404     LoggerE("Couldn't register callback for read value %d (%s)", ret, get_error_message(ret));
405   }
406   ReportSuccess(out);
407 }
408
409 void BluetoothGATTService::WriteValue(const picojson::value& args, picojson::object& out) {
410   ScopeLogger();
411   CHECK_BACKWARD_COMPABILITY_PRIVILEGE_ACCESS(Privilege::kBluetooth, Privilege::kBluetoothAdmin,
412                                               &out);
413
414   const std::string& address = args.get("address").get<std::string>();
415   if (!IsStillConnected(address)) {
416     LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Device is not connected"), &out,
417                       ("Device with address %s is no longer connected", address.c_str()));
418     return;
419   }
420
421   const double callback_handle = util::GetAsyncCallbackHandle(args);
422   const picojson::array& value_array = args.get("value").get<picojson::array>();
423
424   int value_size = value_array.size();
425   std::unique_ptr<char[]> value_data(new char[value_size]);
426   for (int i = 0; i < value_size; ++i) {
427     value_data[i] = (int)value_array[i].get<double>();
428   }
429
430   struct Data {
431     double callback_handle;
432     BluetoothGATTService* service;
433   };
434
435   bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get("handle").get<double>());
436
437   auto write_value = [](int result, bt_gatt_h handle, void* user_data) -> void {
438     ScopeLogger("Entered into asynchronous function, write_value");
439     Data* data = static_cast<Data*>(user_data);
440     double callback_handle = data->callback_handle;
441     BluetoothGATTService* service = data->service;
442     delete data;
443
444     PlatformResult ret = PlatformResult(ErrorCode::NO_ERROR);
445     if (BT_ERROR_NONE != result) {
446       ret = util::GetBluetoothError(result, "Error while getting value");
447     }
448
449     std::shared_ptr<picojson::value> response =
450         std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
451     if (ret.IsSuccess()) {
452       ReportSuccess(response->get<picojson::object>());
453     } else {
454       LogAndReportError(ret, &response->get<picojson::object>());
455     }
456     TaskQueue::GetInstance().Async<picojson::value>(
457         [service, callback_handle](const std::shared_ptr<picojson::value>& response) {
458           service->instance_.SyncResponse(callback_handle, response);
459         },
460         response);
461   };
462
463   int ret = bt_gatt_set_value(handle, value_data.get(), value_size);
464
465   if (BT_ERROR_NONE != ret) {
466     std::shared_ptr<picojson::value> response =
467         std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
468     LogAndReportError(util::GetBluetoothError(ret, "Failed to set value"),
469                       &response->get<picojson::object>(),
470                       ("bt_gatt_set_value error: %d (%s)", ret, get_error_message(ret)));
471     TaskQueue::GetInstance().Async<picojson::value>(
472         [this, callback_handle](const std::shared_ptr<picojson::value>& response) {
473           instance_.SyncResponse(callback_handle, response);
474         },
475         response);
476   } else {
477     Data* user_data = new Data{callback_handle, this};
478     ret = bt_gatt_client_write_value(handle, write_value, user_data);
479     if (BT_ERROR_NONE != ret) {
480       delete user_data;
481       LoggerE("Couldn't register callback for write value %d (%s)", ret, get_error_message(ret));
482     }
483   }
484   ReportSuccess(out);
485 }
486
487 void BluetoothGATTService::AddValueChangeListener(const picojson::value& args,
488                                                   picojson::object& out) {
489   ScopeLogger();
490   const auto& address = args.get("address").get<std::string>();
491   if (!IsStillConnected(address)) {
492     LogAndReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Device is not connected"), &out,
493                       ("Device with address %s is no longer connected", address.c_str()));
494     return;
495   }
496
497   bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get(kHandle).get<double>());
498
499   int ret = bt_gatt_client_set_characteristic_value_changed_cb(handle, OnCharacteristicValueChanged,
500                                                                this);
501   if (BT_ERROR_NONE != ret) {
502     LogAndReportError(util::GetBluetoothError(ret, "Failed to register listener"), &out,
503                       ("bt_gatt_client_set_characteristic_value_changed_cb() failed with: %d (%s)",
504                        ret, get_error_message(ret)));
505   } else {
506     gatt_characteristic_.push_back(handle);
507     ReportSuccess(out);
508   }
509 }
510
511 void BluetoothGATTService::RemoveValueChangeListener(const picojson::value& args,
512                                                      picojson::object& out) {
513   ScopeLogger();
514   const auto& address = args.get("address").get<std::string>();
515   if (!IsStillConnected(address)) {
516     LoggerW("Device with address %s is no longer connected", address.c_str());
517   } else {
518     bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get(kHandle).get<double>());
519
520     int ret = bt_gatt_client_unset_characteristic_value_changed_cb(handle);
521
522     if (BT_ERROR_NONE != ret) {
523       LoggerW("bt_gatt_client_unset_characteristic_value_changed_cb() failed with: %d (%s)", ret,
524               get_error_message(ret));
525     } else {
526       gatt_characteristic_.erase(
527           std::remove(gatt_characteristic_.begin(), gatt_characteristic_.end(), handle),
528           gatt_characteristic_.end());
529     }
530   }
531   ReportSuccess(out);
532 }
533
534 common::PlatformResult BluetoothGATTService::GetServiceAllUuids(const std::string& address,
535                                                                 picojson::array* array) {
536   ScopeLogger();
537
538   bt_gatt_client_h client = GetGattClient(address);
539
540   if (nullptr == client) {
541     return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to create client");
542   }
543
544   auto foreach_callback = [](int total, int index, bt_gatt_h gatt_handle, void* user_data) -> bool {
545     ScopeLogger("Entered into asynchronous function, foreach_callback, total: %d, index: %d", total,
546                 index);
547
548     char* uuid = nullptr;
549     int ret = bt_gatt_get_uuid(gatt_handle, &uuid);
550
551     if (BT_ERROR_NONE != ret || nullptr == uuid) {
552       LoggerE("Failed to get UUID: %d", ret);
553     } else {
554       std::string u = std::string(uuid);
555       free(uuid);
556       if (u.length() > 4) {  // 128-bit UUID, needs to be converted to 16-bit
557         u = u.substr(4, 4);
558       }
559       static_cast<picojson::array*>(user_data)->push_back(picojson::value(u));
560     }
561
562     return true;
563   };
564
565   int ret = bt_gatt_client_foreach_services(client, foreach_callback, array);
566
567   if (BT_ERROR_NONE == ret) {
568     return PlatformResult(ErrorCode::NO_ERROR);
569   } else {
570     return util::GetBluetoothError(ret, "Failed to get UUIDS");
571   }
572 }
573
574 void BluetoothGATTService::OnCharacteristicValueChanged(bt_gatt_h characteristic, char* value,
575                                                         int length, void* user_data) {
576   ScopeLogger("characteristic: [%p], len: [%d], user_data: [%p]", characteristic, length,
577               user_data);
578
579   auto service = static_cast<BluetoothGATTService*>(user_data);
580
581   if (!service) {
582     LoggerE("user_data is NULL");
583     return;
584   }
585
586   picojson::value result = picojson::value(picojson::object());
587   picojson::object& result_obj = result.get<picojson::object>();
588
589   result_obj.insert(std::make_pair(kHandle, picojson::value((double)(long)characteristic)));
590
591   picojson::value byte_array = picojson::value(picojson::array());
592   picojson::array& byte_array_obj = byte_array.get<picojson::array>();
593
594   for (int i = 0; i < length; ++i) {
595     byte_array_obj.push_back(picojson::value(std::to_string(value[i])));
596   }
597
598   ReportSuccess(byte_array, result_obj);
599
600   service->instance_.FireEvent(kOnValueChanged, result);
601 }
602
603 }  // namespace bluetooth
604 }  // namespace extension