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_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 kHandle = "handle";
42 const std::string kAddress = "address";
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";
54 const std::string kOnValueChanged = "BluetoothGATTCharacteristicValueChangeListener";
56 bool IsProperty(int propertyBits, bt_gatt_property_e property) {
57 return (propertyBits & property) != 0;
61 BluetoothGATTService::BluetoothGATTService(BluetoothInstance& instance) : instance_(instance) {
65 BluetoothGATTService::~BluetoothGATTService() {
68 for (auto it : gatt_characteristic_) {
69 // unregister callback, ignore errors
70 bt_gatt_client_unset_characteristic_value_changed_cb(it);
73 for (auto it : gatt_clients_) {
74 LoggerD("destroying client for address: %s", it.first.c_str());
75 bt_gatt_client_destroy(it.second);
79 bool BluetoothGATTService::IsStillConnected(const std::string& address) {
80 auto it = gatt_clients_.find(address);
81 return gatt_clients_.end() != it;
84 bt_gatt_client_h BluetoothGATTService::GetGattClient(const std::string& address) {
87 bt_gatt_client_h client = nullptr;
89 const auto it = gatt_clients_.find(address);
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);
96 gatt_clients_.insert(std::make_pair(address, client));
99 LoggerD("Client already created");
106 // this method should be used to inform this object that some device was disconnected
107 void BluetoothGATTService::TryDestroyClient(const std::string& address) {
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);
115 LoggerD("Client for address: %s does not exist, no need for deletion", address.c_str());
119 PlatformResult BluetoothGATTService::GetSpecifiedGATTService(const std::string& address,
120 const std::string& uuid,
121 picojson::object* result) {
124 bt_gatt_client_h client = GetGattClient(address);
126 if (nullptr == client) {
127 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Failed to create the GATT client's handle");
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);
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)));
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)));
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)));
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);
161 void BluetoothGATTService::GetServices(const picojson::value& args, picojson::object& out) {
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>();
167 picojson::array array;
168 PlatformResult ret = GetServicesHelper(handle, address, &array);
170 LogAndReportError(ret, &out, ("Error while getting services"));
172 ReportSuccess(picojson::value(array), out);
176 PlatformResult BluetoothGATTService::GetServicesHelper(bt_gatt_h handle, const std::string& address,
177 picojson::array* array) {
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()));
185 int ret = bt_gatt_service_foreach_included_services(
187 [](int total, int index, bt_gatt_h gatt_handle, void* data) {
189 "Entered into asynchronous function, argument in "
190 "bt_gatt_service_foreach_included_services");
192 picojson::value result = picojson::value(picojson::object());
193 picojson::object& result_obj = result.get<picojson::object>();
195 char* uuid = nullptr;
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)));
201 result_obj.insert(std::make_pair(kUuid, picojson::value("FFFF")));
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);
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");
215 return PlatformResult(ErrorCode::NO_ERROR);
218 void BluetoothGATTService::GetCharacteristics(const picojson::value& args, picojson::object& out) {
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>();
225 picojson::array array;
226 PlatformResult ret = GetCharacteristicsHelper(handle, address, uuid, &array);
228 LogAndReportError(ret, &out, ("Error while getting characteristics"));
230 ReportSuccess(picojson::value(array), out);
234 PlatformResult BluetoothGATTService::GetCharacteristicsHelper(bt_gatt_h handle,
235 const std::string& address,
236 const std::string& uuid,
237 picojson::array* array) {
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()));
246 picojson::array* array;
247 PlatformResult* platform_res;
250 PlatformResult platform_result = PlatformResult(ErrorCode::NO_ERROR);
251 Data user_data = {array, &platform_result};
253 int ret = bt_gatt_service_foreach_characteristics(
255 [](int total, int index, bt_gatt_h gatt_handle, void* data) {
257 "Entered into asynchronous function, bt_gatt_service_foreach_characteristics's "
259 Data* user_data = static_cast<Data*>(data);
260 picojson::array* array = user_data->array;
261 PlatformResult* platform_result = user_data->platform_res;
263 picojson::value result = picojson::value(picojson::object());
264 picojson::object& result_obj = result.get<picojson::object>();
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)));
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(
275 [](int total, int index, bt_gatt_h desc_handle, void* data) {
277 picojson::array& desc_array = *(static_cast<picojson::array*>(data));
279 picojson::value desc = picojson::value(picojson::object());
280 picojson::object& desc_obj = desc.get<picojson::object>();
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);
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);
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");
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(
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(
318 picojson::value(IsProperty(property_bits, BT_GATT_PROPERTY_EXTENDED_PROPERTIES))));
320 array->push_back(result);
323 static_cast<void*>(&user_data));
324 if (platform_result.IsError()) {
325 return platform_result;
327 if (BT_ERROR_NONE != ret) {
328 LoggerE("Failed (%d)", ret);
329 return util::GetBluetoothError(ret, "Failed while getting characteristic");
332 return PlatformResult(ErrorCode::NO_ERROR);
335 void BluetoothGATTService::ReadValue(const picojson::value& args, picojson::object& out) {
337 CHECK_BACKWARD_COMPABILITY_PRIVILEGE_ACCESS(Privilege::kBluetooth, Privilege::kBluetoothAdmin,
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()));
347 const double callback_handle = util::GetAsyncCallbackHandle(args);
349 double callback_handle;
350 BluetoothGATTService* service;
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>());
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;
363 PlatformResult plarform_res = PlatformResult(ErrorCode::NO_ERROR);
365 picojson::value byte_array = picojson::value(picojson::array());
366 picojson::array& byte_array_obj = byte_array.get<picojson::array>();
368 if (BT_ERROR_NONE != result) {
369 plarform_res = util::GetBluetoothError(result, "Error while reading value");
371 char* value = nullptr;
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");
377 for (int i = 0; i < length; i++) {
378 byte_array_obj.push_back(picojson::value(std::to_string(value[i])));
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>());
392 LogAndReportError(plarform_res, &response->get<picojson::object>());
394 TaskQueue::GetInstance().Async<picojson::value>(
395 [service, callback_handle](const std::shared_ptr<picojson::value>& response) {
396 service->instance_.SyncResponse(callback_handle, response);
400 int ret = bt_gatt_client_read_value(handle, read_value, (void*)user_data);
401 if (BT_ERROR_NONE != ret) {
404 LoggerE("Couldn't register callback for read value %d (%s)", ret, get_error_message(ret));
409 void BluetoothGATTService::WriteValue(const picojson::value& args, picojson::object& out) {
411 CHECK_BACKWARD_COMPABILITY_PRIVILEGE_ACCESS(Privilege::kBluetooth, Privilege::kBluetoothAdmin,
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()));
421 const double callback_handle = util::GetAsyncCallbackHandle(args);
422 const picojson::array& value_array = args.get("value").get<picojson::array>();
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>();
431 double callback_handle;
432 BluetoothGATTService* service;
435 bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get("handle").get<double>());
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;
444 PlatformResult ret = PlatformResult(ErrorCode::NO_ERROR);
445 if (BT_ERROR_NONE != result) {
446 ret = util::GetBluetoothError(result, "Error while getting value");
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>());
454 LogAndReportError(ret, &response->get<picojson::object>());
456 TaskQueue::GetInstance().Async<picojson::value>(
457 [service, callback_handle](const std::shared_ptr<picojson::value>& response) {
458 service->instance_.SyncResponse(callback_handle, response);
463 int ret = bt_gatt_set_value(handle, value_data.get(), value_size);
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);
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) {
481 LoggerE("Couldn't register callback for write value %d (%s)", ret, get_error_message(ret));
487 void BluetoothGATTService::AddValueChangeListener(const picojson::value& args,
488 picojson::object& out) {
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()));
497 bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get(kHandle).get<double>());
499 int ret = bt_gatt_client_set_characteristic_value_changed_cb(handle, OnCharacteristicValueChanged,
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)));
506 gatt_characteristic_.push_back(handle);
511 void BluetoothGATTService::RemoveValueChangeListener(const picojson::value& args,
512 picojson::object& out) {
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());
518 bt_gatt_h handle = (bt_gatt_h) static_cast<long>(args.get(kHandle).get<double>());
520 int ret = bt_gatt_client_unset_characteristic_value_changed_cb(handle);
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));
526 gatt_characteristic_.erase(
527 std::remove(gatt_characteristic_.begin(), gatt_characteristic_.end(), handle),
528 gatt_characteristic_.end());
534 common::PlatformResult BluetoothGATTService::GetServiceAllUuids(const std::string& address,
535 picojson::array* array) {
538 bt_gatt_client_h client = GetGattClient(address);
540 if (nullptr == client) {
541 return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to create client");
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,
548 char* uuid = nullptr;
549 int ret = bt_gatt_get_uuid(gatt_handle, &uuid);
551 if (BT_ERROR_NONE != ret || nullptr == uuid) {
552 LoggerE("Failed to get UUID: %d", ret);
554 std::string u = std::string(uuid);
556 if (u.length() > 4) { // 128-bit UUID, needs to be converted to 16-bit
559 static_cast<picojson::array*>(user_data)->push_back(picojson::value(u));
565 int ret = bt_gatt_client_foreach_services(client, foreach_callback, array);
567 if (BT_ERROR_NONE == ret) {
568 return PlatformResult(ErrorCode::NO_ERROR);
570 return util::GetBluetoothError(ret, "Failed to get UUIDS");
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,
579 auto service = static_cast<BluetoothGATTService*>(user_data);
582 LoggerE("user_data is NULL");
586 picojson::value result = picojson::value(picojson::object());
587 picojson::object& result_obj = result.get<picojson::object>();
589 result_obj.insert(std::make_pair(kHandle, picojson::value((double)(long)characteristic)));
591 picojson::value byte_array = picojson::value(picojson::array());
592 picojson::array& byte_array_obj = byte_array.get<picojson::array>();
594 for (int i = 0; i < length; ++i) {
595 byte_array_obj.push_back(picojson::value(std::to_string(value[i])));
598 ReportSuccess(byte_array, result_obj);
600 service->instance_.FireEvent(kOnValueChanged, result);
603 } // namespace bluetooth
604 } // namespace extension