2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "bluetooth/bluetooth_gatt_client_service.h"
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"
27 #include "bluetooth/bluetooth_instance.h"
28 #include "bluetooth/bluetooth_privilege.h"
29 #include "bluetooth/bluetooth_util.h"
34 using common::PlatformResult;
35 using common::ErrorCode;
36 using common::TaskQueue;
37 using namespace common::tools;
40 const std::string kUuid = "uuid";
41 const std::string kServiceUuid = "serviceUuid";
42 const std::string kHandle = "handle";
43 const std::string kAddress = "address";
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";
55 const std::string kOnValueChanged = "BluetoothGATTCharacteristicValueChangeListener";
57 bool IsProperty(int propertyBits, bt_gatt_property_e property) {
58 return (propertyBits & property) != 0;
62 BluetoothGATTClientService::BluetoothGATTClientService(BluetoothInstance& instance)
63 : instance_(instance) {
67 BluetoothGATTClientService::~BluetoothGATTClientService() {
70 for (auto it : gatt_characteristic_) {
71 // unregister callback, ignore errors
72 bt_gatt_client_unset_characteristic_value_changed_cb(it);
75 for (auto it : gatt_clients_) {
76 LoggerD("destroying client for address: %s", it.first.c_str());
77 bt_gatt_client_destroy(it.second);
81 bool BluetoothGATTClientService::IsStillConnected(const std::string& address) {
82 auto it = gatt_clients_.find(address);
83 return gatt_clients_.end() != it;
86 bt_gatt_client_h BluetoothGATTClientService::GetGattClient(const std::string& address) {
89 bt_gatt_client_h client = nullptr;
91 const auto it = gatt_clients_.find(address);
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);
98 gatt_clients_.insert(std::make_pair(address, client));
101 LoggerD("Client already created");
108 // this method should be used to inform this object that some device was disconnected
109 void BluetoothGATTClientService::TryDestroyClient(const std::string& address) {
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);
117 LoggerD("Client for address: %s does not exist, no need for deletion", address.c_str());
121 PlatformResult BluetoothGATTClientService::GetSpecifiedGATTClient(const std::string& address,
123 picojson::object* result) {
126 bt_gatt_client_h client = GetGattClient(address);
128 if (nullptr == client) {
129 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to create the GATT client's handle");
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);
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)));
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)));
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)));
155 * BACKWARD COMPATIBILITY
157 * BluetoothGATTClientService::uuid has always been set to source format in this
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);
169 void BluetoothGATTClientService::GetServices(const picojson::value& args, picojson::object& out) {
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>();
175 picojson::array array;
176 PlatformResult ret = GetServicesHelper(handle, address, &array);
178 LogAndReportError(ret, &out, ("Error while getting services"));
180 ReportSuccess(picojson::value(array), out);
184 PlatformResult BluetoothGATTClientService::GetServicesHelper(bt_gatt_h handle,
185 const std::string& address,
186 picojson::array* array) {
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()));
194 int ret = bt_gatt_service_foreach_included_services(
196 [](int total, int index, bt_gatt_h gatt_handle, void* data) {
198 "Entered into asynchronous function, argument in "
199 "bt_gatt_service_foreach_included_services");
201 picojson::value result = picojson::value{picojson::object{}};
202 picojson::object& result_obj = result.get<picojson::object>();
204 const auto uuid = UUID::createFromGatt(gatt_handle);
207 * BACKWARD COMPATIBILITY
209 * UUID has always been set to source format in this function.
211 result_obj.insert(std::make_pair(kUuid, picojson::value{uuid->uuid_in_source_format}));
213 std::make_pair(kServiceUuid, picojson::value{uuid->uuid_in_source_format}));
215 result_obj.insert(std::make_pair(kUuid, picojson::value{"0xFFFF"}));
216 result_obj.insert(std::make_pair(kServiceUuid, picojson::value{}));
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);
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");
230 return PlatformResult(ErrorCode::NO_ERROR);
233 void BluetoothGATTClientService::GetCharacteristics(const picojson::value& args,
234 picojson::object& out) {
237 bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get("handle").get<double>());
239 const std::string& address = args.get("address").get<std::string>();
241 picojson::array array;
242 PlatformResult ret = GetCharacteristicsHelper(handle, address, &array);
244 LogAndReportError(ret, &out, ("Error while getting characteristics"));
246 ReportSuccess(picojson::value(array), out);
250 PlatformResult BluetoothGATTClientService::GetCharacteristicsHelper(bt_gatt_h handle,
251 const std::string& address,
252 picojson::array* array) {
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()));
261 picojson::array* array;
262 PlatformResult* platform_res;
265 PlatformResult platform_result = PlatformResult(ErrorCode::NO_ERROR);
266 Data user_data = {array, &platform_result};
268 int ret = bt_gatt_service_foreach_characteristics(
270 [](int total, int index, bt_gatt_h gatt_handle, void* data) {
272 "Entered into asynchronous function, bt_gatt_service_foreach_characteristics;"
275 Data* user_data = static_cast<Data*>(data);
276 picojson::array* array = user_data->array;
277 PlatformResult* platform_result = user_data->platform_res;
279 picojson::value result = picojson::value(picojson::object());
280 picojson::object& result_obj = result.get<picojson::object>();
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)));
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(
291 [](int total, int index, bt_gatt_h desc_handle, void* data) {
293 "Entered into asynchronous function, bt_gatt_characteristic_foreach_descriptors;"
296 picojson::array& desc_array = *(static_cast<picojson::array*>(data));
298 picojson::value desc = picojson::value(picojson::object());
299 picojson::object& desc_obj = desc.get<picojson::object>();
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)));
304 auto uuid = UUID::createFromGatt(desc_handle);
307 std::make_pair(kUuid, picojson::value{uuid->ShortestPossibleFormat()}));
309 desc_obj.insert(std::make_pair(kUuid, picojson::value{}));
312 desc_array.push_back(desc);
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);
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");
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(
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(
346 picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_EXTENDED_PROPERTIES))));
348 auto uuid = UUID::createFromGatt(gatt_handle);
350 result_obj.insert(std::make_pair(kUuid, picojson::value{uuid->ShortestPossibleFormat()}));
352 LoggerW("bt_gatt_get_uuid error: %d (%s)", err, get_error_message(err));
353 result_obj.insert(std::make_pair(kUuid, picojson::value{}));
356 array->push_back(result);
359 static_cast<void*>(&user_data));
360 if (platform_result.IsError()) {
361 return platform_result;
363 if (BT_ERROR_NONE != ret) {
364 LoggerE("Failed (%d)", ret);
365 return util::GetBluetoothError(ret, "Failed while getting characteristic");
368 return PlatformResult(ErrorCode::NO_ERROR);
371 void BluetoothGATTClientService::ReadValue(const picojson::value& args, picojson::object& out) {
373 CHECK_BACKWARD_COMPABILITY_PRIVILEGE_ACCESS(Privilege::kBluetooth, Privilege::kBluetoothAdmin,
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()));
383 const double callback_handle = util::GetAsyncCallbackHandle(args);
385 double callback_handle;
386 BluetoothGATTClientService* service;
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>());
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;
399 PlatformResult plarform_res = PlatformResult(ErrorCode::NO_ERROR);
401 picojson::value byte_array = picojson::value(picojson::array());
402 picojson::array& byte_array_obj = byte_array.get<picojson::array>();
404 if (BT_ERROR_NONE != result) {
405 plarform_res = util::GetBluetoothError(result, "Error while reading value");
407 char* value = nullptr;
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");
413 for (int i = 0; i < length; i++) {
414 byte_array_obj.push_back(picojson::value(std::to_string(value[i])));
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>());
428 LogAndReportError(plarform_res, &response->get<picojson::object>());
430 TaskQueue::GetInstance().Async<picojson::value>(
431 [service, callback_handle](const std::shared_ptr<picojson::value>& response) {
432 service->instance_.SyncResponse(callback_handle, response);
436 int ret = bt_gatt_client_read_value(handle, read_value, (void*)user_data);
437 if (BT_ERROR_NONE != ret) {
440 LoggerE("Couldn't register callback for read value %d (%s)", ret, get_error_message(ret));
445 void BluetoothGATTClientService::WriteValue(const picojson::value& args, picojson::object& out) {
447 CHECK_BACKWARD_COMPABILITY_PRIVILEGE_ACCESS(Privilege::kBluetooth, Privilege::kBluetoothAdmin,
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()));
457 const double callback_handle = util::GetAsyncCallbackHandle(args);
458 const picojson::array& value_array = args.get("value").get<picojson::array>();
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>();
467 double callback_handle;
468 BluetoothGATTClientService* service;
471 bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get("handle").get<double>());
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;
480 PlatformResult ret = PlatformResult(ErrorCode::NO_ERROR);
481 if (BT_ERROR_NONE != result) {
482 ret = util::GetBluetoothError(result, "Error while getting value");
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>());
490 LogAndReportError(ret, &response->get<picojson::object>());
492 TaskQueue::GetInstance().Async<picojson::value>(
493 [service, callback_handle](const std::shared_ptr<picojson::value>& response) {
494 service->instance_.SyncResponse(callback_handle, response);
499 int ret = bt_gatt_set_value(handle, value_data.get(), value_size);
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);
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) {
517 LoggerE("Couldn't register callback for write value %d (%s)", ret, get_error_message(ret));
523 void BluetoothGATTClientService::AddValueChangeListener(const picojson::value& args,
524 picojson::object& out) {
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()));
533 bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get(kHandle).get<double>());
535 int ret = bt_gatt_client_set_characteristic_value_changed_cb(handle, OnCharacteristicValueChanged,
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)));
542 gatt_characteristic_.push_back(handle);
547 void BluetoothGATTClientService::RemoveValueChangeListener(const picojson::value& args,
548 picojson::object& out) {
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());
554 bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get(kHandle).get<double>());
556 int ret = bt_gatt_client_unset_characteristic_value_changed_cb(handle);
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));
562 gatt_characteristic_.erase(
563 std::remove(gatt_characteristic_.begin(), gatt_characteristic_.end(), handle),
564 gatt_characteristic_.end());
570 common::PlatformResult BluetoothGATTClientService::GetServiceAllUuids(const std::string& address,
571 picojson::array* array) {
574 bt_gatt_client_h client = GetGattClient(address);
576 if (nullptr == client) {
577 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to create client");
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,
584 auto& uuids = *static_cast<picojson::array*>(user_data);
585 auto uuid = UUID::createFromGatt(gatt_handle);
588 * BACKWARD COMPATIBILITY
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.
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.
598 uuids.push_back(picojson::value{uuid->ShortestPossibleFormat()});
600 LoggerE("Couldn't get UUID from bt_gatt_h");
606 int ret = bt_gatt_client_foreach_services(client, foreach_callback, array);
608 if (BT_ERROR_NONE == ret) {
609 return PlatformResult(ErrorCode::NO_ERROR);
611 return util::GetBluetoothError(ret, "Failed to get UUIDS");
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,
620 auto service = static_cast<BluetoothGATTClientService*>(user_data);
623 LoggerE("user_data is NULL");
627 picojson::value result = picojson::value(picojson::object());
628 picojson::object& result_obj = result.get<picojson::object>();
630 result_obj.insert(std::make_pair(kHandle, picojson::value((double)(long)characteristic)));
632 picojson::value byte_array = picojson::value(picojson::array());
633 picojson::array& byte_array_obj = byte_array.get<picojson::array>();
635 for (int i = 0; i < length; ++i) {
636 byte_array_obj.push_back(picojson::value(std::to_string(value[i])));
639 ReportSuccess(byte_array, result_obj);
641 service->instance_.FireEvent(kOnValueChanged, result);
644 } // namespace bluetooth
645 } // namespace extension