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