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_le_adapter.h"
19 #include "common/tools.h"
20 #include "common/logger.h"
22 #include "bluetooth/bluetooth_instance.h"
23 #include "bluetooth/bluetooth_le_device.h"
24 #include "bluetooth/bluetooth_util.h"
31 class ParsedDataHolder {
33 ParsedDataHolder() : valid_(false) {}
34 virtual ~ParsedDataHolder() {}
49 class BluetoothLEServiceData : public ParsedDataHolder {
51 BluetoothLEServiceData()
52 : ParsedDataHolder() {
55 const std::string& uuid() const {
59 const std::string& data() const {
63 static bool Construct(const picojson::value& obj,
64 BluetoothLEServiceData* out) {
65 if (!obj.is<picojson::object>() ||
66 !ParseUUID(obj, out) ||
67 !ParseData(obj, out)) {
77 static bool ParseUUID(const picojson::value& obj,
78 BluetoothLEServiceData* out) {
80 const auto& uuid = obj.get("serviceuuid");
81 if (uuid.is<std::string>()) {
82 out->uuid_ = uuid.get<std::string>();
90 static bool ParseData(const picojson::value& obj,
91 BluetoothLEServiceData* out) {
93 const auto& data = obj.get("data");
94 if (data.is<std::string>()) {
95 out->data_ = data.get<std::string>();
107 class BluetoothLEManufacturerData : public ParsedDataHolder {
109 BluetoothLEManufacturerData()
110 : ParsedDataHolder(),
115 const std::string& id() const {
119 const unsigned char* const data() const {
123 const int data_length() const {
127 static bool Construct(const picojson::value& obj,
128 BluetoothLEManufacturerData* out) {
130 if (!obj.is<picojson::object>() ||
131 !ParseId(obj, out) ||
132 !ParseData(obj, out)) {
141 ~BluetoothLEManufacturerData() {
150 static bool ParseId(const picojson::value& obj,
151 BluetoothLEManufacturerData* out) {
153 const auto& id = obj.get("id");
154 if (id.is<std::string>()) {
155 out->id_ = id.get<std::string>();
163 static bool ParseData(const picojson::value& obj,
164 BluetoothLEManufacturerData* out) {
167 const auto& val_data = obj.get("data");
169 if (val_data.is<std::string>()) {
170 const std::string& str_data = val_data.get<std::string>();
171 const char* p_data = str_data.c_str();
172 int size = str_data.length();
173 if (size > 2 && (str_data.find("0x", 0) == 0 || str_data.find("0X", 0) == 0)) {
177 out->data_length_ = size / 2;
178 out->data_ = new unsigned char[out->data_length_];
179 common::tools::HexToBin(p_data, size, out->data_, out->data_length_);
187 unsigned char* data_;
191 class BluetoothLEAdvertiseData : public ParsedDataHolder {
193 BluetoothLEAdvertiseData()
194 : ParsedDataHolder(),
195 include_name_(false),
196 appearance_(0), // 0 means unknown
197 include_tx_power_level_(false) {
200 bool include_name() const {
201 return include_name_;
204 const std::vector<std::string>& service_uuids() const {
205 return service_uuids_;
208 const std::vector<std::string>& solicitation_uuids() const {
209 return solicitation_uuids_;
212 int appearance() const {
216 bool include_tx_power_level() const {
217 return include_tx_power_level_;
220 const BluetoothLEServiceData& service_data() const {
221 return service_data_;
224 const BluetoothLEManufacturerData& manufacturer_data() const {
225 return manufacturer_data_;
228 static bool Construct(const picojson::value& obj,
229 BluetoothLEAdvertiseData* out) {
231 if (!obj.is<picojson::object>() ||
232 !ParseIncludeName(obj, out) ||
233 !ParseServiceUUIDs(obj, out) ||
234 !ParseSolicitationUUIDs(obj, out) ||
235 !ParseAppearance(obj, out) ||
236 !ParseIncludeTxPowerLevel(obj, out) ||
237 !ParseServiceData(obj, out) ||
238 !ParseManufacturerData(obj, out)) {
248 static bool ParseIncludeName(const picojson::value& obj,
249 BluetoothLEAdvertiseData* out) {
251 const auto& include_name = obj.get("includeName");
252 if (include_name.is<bool>()) {
253 out->include_name_ = include_name.get<bool>();
254 } else if (!include_name.is<picojson::null>()) {
261 static bool ParseServiceUUIDs(const picojson::value& obj,
262 BluetoothLEAdvertiseData* out) {
264 const auto& service_uuids = obj.get("serviceuuids");
265 if (service_uuids.is<picojson::array>()) {
266 for (const auto& i : service_uuids.get<picojson::array>()) {
267 if (i.is<std::string>()) {
268 out->service_uuids_.push_back(i.get<std::string>());
273 } else if (!service_uuids.is<picojson::null>()) {
280 static bool ParseSolicitationUUIDs(const picojson::value& obj,
281 BluetoothLEAdvertiseData* out) {
283 const auto& solicitation_uuids = obj.get("solicitationuuids");
284 if (solicitation_uuids.is<picojson::array>()) {
285 for (const auto& i : solicitation_uuids.get<picojson::array>()) {
286 if (i.is<std::string>()) {
287 out->solicitation_uuids_.push_back(i.get<std::string>());
292 } else if (!solicitation_uuids.is<picojson::null>()) {
299 static bool ParseAppearance(const picojson::value& obj,
300 BluetoothLEAdvertiseData* out) {
302 const auto& appearance = obj.get("appearance");
303 if (appearance.is<double>()) {
304 out->appearance_ = static_cast<decltype(appearance_)>(appearance.get<double>());
305 } else if (!appearance.is<picojson::null>()) {
312 static bool ParseIncludeTxPowerLevel(const picojson::value& obj,
313 BluetoothLEAdvertiseData* out) {
315 const auto& include_tx_power_level = obj.get("includeTxPowerLevel");
316 if (include_tx_power_level.is<bool>()) {
317 out->include_tx_power_level_ = include_tx_power_level.get<bool>();
318 } else if (!include_tx_power_level.is<picojson::null>()) {
325 static bool ParseServiceData(const picojson::value& obj,
326 BluetoothLEAdvertiseData* out) {
328 const auto& service_data = obj.get("serviceData");
329 BluetoothLEServiceData data;
330 if (BluetoothLEServiceData::Construct(service_data, &data)) {
331 out->service_data_ = std::move(data);
332 } else if (!service_data.is<picojson::null>()) {
339 static bool ParseManufacturerData(const picojson::value& obj,
340 BluetoothLEAdvertiseData* out) {
342 const auto& manufacturer_data = obj.get("manufacturerData");
343 BluetoothLEManufacturerData data;
344 if (BluetoothLEManufacturerData::Construct(manufacturer_data, &data)) {
345 out->manufacturer_data_ = std::move(data);
346 } else if (!manufacturer_data.is<picojson::null>()) {
354 std::vector<std::string> service_uuids_;
355 std::vector<std::string> solicitation_uuids_;
357 bool include_tx_power_level_;
358 BluetoothLEServiceData service_data_;
359 BluetoothLEManufacturerData manufacturer_data_;
364 bool ToBool(bt_adapter_le_state_e state) {
365 return (BT_ADAPTER_LE_ENABLED == state) ? true : false;
370 const std::string kAction = "action";
371 const std::string kData = "data";
373 const std::string kOnScanSuccess = "onsuccess";
374 const std::string kOnScanError = "onerror";
375 const std::string kScanEvent = "BluetoothLEScanCallback";
377 const std::string kOnAdvertiseState = "onstate";
378 const std::string kOnAdvertiseError = "onerror";
379 const std::string kAdvertiseEvent = "BluetoothLEAdvertiseCallback";
383 using common::ErrorCode;
384 using common::PlatformResult;
385 using common::tools::ReportError;
386 using common::tools::ReportSuccess;
388 BluetoothLEAdapter::BluetoothLEAdapter(BluetoothInstance& instance)
389 : instance_(instance),
392 bt_advertiser_(nullptr) {
395 bt_adapter_le_state_e le_state = BT_ADAPTER_LE_DISABLED;
397 int ret = bt_adapter_le_get_state(&le_state);
399 if (BT_ERROR_NONE == ret) {
400 enabled_ = ToBool(le_state);
402 ret = bt_adapter_le_set_state_changed_cb(OnStateChanged, this);
403 if (BT_ERROR_NONE != ret) {
404 LoggerE("Failed to register BTLE state changed listener.");
407 LoggerE("Failed to obtain current state of BTLE.");
411 BluetoothLEAdapter::~BluetoothLEAdapter() {
413 bt_adapter_le_unset_state_changed_cb();
415 bt_adapter_le_stop_scan();
417 if (bt_advertiser_) {
418 bt_adapter_le_stop_advertising(bt_advertiser_);
419 bt_adapter_le_destroy_advertiser(bt_advertiser_);
423 void BluetoothLEAdapter::StartScan(const picojson::value& data, picojson::object& out) {
426 int ret = bt_adapter_le_start_scan(OnScanResult, this);
428 if (BT_ERROR_NONE != ret) {
429 if (BT_ERROR_NOW_IN_PROGRESS == ret) {
431 PlatformResult(ErrorCode::INVALID_STATE_ERR, "Scan already in progress"), &out,
432 ("Scan in progress %d (%s)", ret, get_error_message(ret)));
435 // other errors are reported asynchronously
436 picojson::value value = picojson::value(picojson::object());
437 picojson::object* data_obj = &value.get<picojson::object>();
438 data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanError)));
440 PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to start scan"), data_obj,
441 ("Failed to start scan: %d (%s)", ret, get_error_message(ret)));
442 instance_.FireEvent(kScanEvent, value);
450 void BluetoothLEAdapter::StopScan(const picojson::value& data, picojson::object& out) {
453 int ret = bt_adapter_le_stop_scan();
455 if (BT_ERROR_NONE != ret && BT_ERROR_NOT_IN_PROGRESS != ret) {
457 PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to stop scan"), &out,
458 ("Failed to stop scan: %d (%s)", ret, get_error_message(ret)));
465 void BluetoothLEAdapter::StartAdvertise(const picojson::value& data, picojson::object& out) {
468 const auto& json_advertise_data = data.get("advertiseData");
469 const auto& json_packet_type = data.get("packetType");
470 const auto& json_mode = data.get("mode");
471 const auto& json_connectable = data.get("connectable");
473 if (!json_advertise_data.is<picojson::object>() ||
474 !json_packet_type.is<std::string>() ||
475 !json_mode.is<std::string>() ||
476 !json_connectable.is<bool>()) {
478 PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Unexpected parameter type"), &out);
482 BluetoothLEAdvertiseData advertise_data;
483 if (!BluetoothLEAdvertiseData::Construct(json_advertise_data, &advertise_data)) {
485 PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Unexpected value of advertise data"), &out);
489 bt_adapter_le_packet_type_e packet_type = BT_ADAPTER_LE_PACKET_ADVERTISING;
491 const auto& str_packet_type = json_packet_type.get<std::string>();
492 if ("ADVERTISE" == str_packet_type) {
493 packet_type = BT_ADAPTER_LE_PACKET_ADVERTISING;
494 } else if ("SCAN_RESPONSE" == str_packet_type) {
495 packet_type = BT_ADAPTER_LE_PACKET_SCAN_RESPONSE;
498 PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Unexpected value of packet type"), &out,
499 ("Wrong packet_type: %s", str_packet_type.c_str()));
504 bt_adapter_le_advertising_mode_e mode = BT_ADAPTER_LE_ADVERTISING_MODE_BALANCED;
506 const auto& str_mode = json_mode.get<std::string>();
507 if ("BALANCED" == str_mode) {
508 mode = BT_ADAPTER_LE_ADVERTISING_MODE_BALANCED;
509 } else if ("LOW_LATENCY" == str_mode) {
510 mode = BT_ADAPTER_LE_ADVERTISING_MODE_LOW_LATENCY;
511 } else if ("LOW_ENERGY" == str_mode) {
512 mode = BT_ADAPTER_LE_ADVERTISING_MODE_LOW_ENERGY;
515 PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Unexpected value of mode"), &out,
516 ("Wrong mode: %s", str_mode.c_str()));
521 if (nullptr != bt_advertiser_) {
523 PlatformResult(ErrorCode::INVALID_STATE_ERR, "Advertise already in progress"), &out);
527 bt_advertiser_h advertiser = nullptr;
529 int ret = bt_adapter_le_create_advertiser(&advertiser);
530 if (BT_ERROR_NONE != ret) {
532 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
533 ("bt_adapter_le_create_advertiser() failed with: %d, (%s)", ret, get_error_message(ret)));
537 std::unique_ptr<std::remove_pointer<bt_advertiser_h>::type,
538 int (*)(bt_advertiser_h)> advertiser_ptr(advertiser, &bt_adapter_le_destroy_advertiser); // automatically release the memory
540 // configure advertiser
541 if (advertise_data.include_name()) {
542 ret = bt_adapter_le_set_advertising_device_name(advertiser, packet_type,
543 advertise_data.include_name());
544 if (BT_ERROR_NONE != ret) {
546 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
547 ("bt_adapter_le_set_advertising_device_name() failed with: %d (%s)", ret, get_error_message(ret)));
552 for (const auto& i : advertise_data.service_uuids()) {
553 ret = bt_adapter_le_add_advertising_service_uuid(advertiser, packet_type,
555 if (BT_ERROR_NONE != ret) {
557 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
558 ("bt_adapter_le_add_advertising_service_uuid() failed with: %d (%s)", ret, get_error_message(ret)));
563 for (const auto& i : advertise_data.solicitation_uuids()) {
564 ret = bt_adapter_le_add_advertising_service_solicitation_uuid(advertiser,
567 if (BT_ERROR_NONE != ret) {
569 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
570 ("bt_adapter_le_add_advertising_service_solicitation_uuid() failed with: %d (%s)",
571 ret, get_error_message(ret)));
576 ret = bt_adapter_le_set_advertising_appearance(advertiser, packet_type,
577 advertise_data.appearance());
578 if (BT_ERROR_NONE != ret) {
580 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
581 ("bt_adapter_le_set_advertising_appearance() failed with: %d (%s)",
582 ret, get_error_message(ret)));
586 if (advertise_data.include_tx_power_level()) {
587 ret = bt_adapter_le_set_advertising_tx_power_level(advertiser, packet_type,
588 advertise_data.include_tx_power_level());
589 if (BT_ERROR_NONE != ret) {
591 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
592 ("bt_adapter_le_set_advertising_tx_power_level() failed with: %d (%s)",
593 ret, get_error_message(ret)));
598 const auto& service_data = advertise_data.service_data();
599 if (service_data.uuid().empty() && service_data.data().empty()) {
600 LoggerD("service data is empty");
602 ret = bt_adapter_le_add_advertising_service_data(advertiser, packet_type,
603 service_data.uuid().c_str(),
604 service_data.data().c_str(),
605 service_data.data().length());
606 if (BT_ERROR_NONE != ret) {
608 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
609 ("bt_adapter_le_add_advertising_service_data() failed with: %d (%s)",
610 ret, get_error_message(ret)));
615 const auto& manufacturer_data = advertise_data.manufacturer_data();
616 if (manufacturer_data.id().empty() && manufacturer_data.data() == nullptr) {
617 LoggerD("manufacturerData is empty");
619 if (manufacturer_data.valid()) {
620 ret = bt_adapter_le_add_advertising_manufacturer_data(advertiser,
622 atoi(manufacturer_data.id().c_str()),
623 (const char*)manufacturer_data.data(),
624 manufacturer_data.data_length());
625 if (BT_ERROR_NONE != ret) {
627 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
628 ("bt_adapter_le_add_advertising_manufacturer_data() failed with: %d (%s)",
629 ret, get_error_message(ret)));
635 ret = bt_adapter_le_set_advertising_mode(advertiser, mode);
636 if (BT_ERROR_NONE != ret) {
638 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
639 ("bt_adapter_le_set_advertising_mode() failed with: %d (%s)",
640 ret, get_error_message(ret)));
644 ret = bt_adapter_le_set_advertising_connectable(advertiser, json_connectable.get<bool>());
645 if (BT_ERROR_NONE != ret) {
647 util::GetBluetoothError(ret, "Failed to create advertiser"), &out,
648 ("bt_adapter_le_set_advertising_connectable() failed with: %d (%s)",
649 ret, get_error_message(ret)));
653 // advertiser is ready, let's start advertising
654 ret = bt_adapter_le_start_advertising_new(advertiser, OnAdvertiseResult, this);
655 if (BT_ERROR_NONE != ret) {
656 if (BT_ERROR_NOW_IN_PROGRESS == ret) {
658 PlatformResult(ErrorCode::INVALID_STATE_ERR, "Advertise already in progress"), &out,
659 ("bt_adapter_le_start_advertising_new error: %d (%s)", ret, get_error_message(ret)));
664 util::GetBluetoothError(ret, "Failed to start advertising"), &out,
665 ("bt_adapter_le_start_advertising_new() failed with: %d (%s)",
666 ret, get_error_message(ret)));
670 // everything went well, we want to store the pointer, so unique_ptr should no longer manage the memory
671 bt_advertiser_ = advertiser_ptr.release();
675 void BluetoothLEAdapter::StopAdvertise(const picojson::value& data, picojson::object& out) {
678 if (nullptr != bt_advertiser_) {
679 int ret = bt_adapter_le_stop_advertising(bt_advertiser_);
680 if (BT_ERROR_NONE != ret && BT_ERROR_NOT_IN_PROGRESS != ret) {
682 PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to stop advertising"), &out,
683 ("bt_adapter_le_stop_advertising() failed with: %d (%s)",
684 ret, get_error_message(ret)));
688 ret = bt_adapter_le_destroy_advertiser(bt_advertiser_);
689 if (BT_ERROR_NONE != ret && BT_ERROR_NOT_IN_PROGRESS != ret) {
691 PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to destroy advertiser"), &out,
692 ("bt_adapter_le_destroy_advertiser() failed with: %d (%s)",
693 ret, get_error_message(ret)));
697 bt_advertiser_ = nullptr;
699 LoggerD("Advertising is not in progress");
705 void BluetoothLEAdapter::OnStateChanged(int result,
706 bt_adapter_le_state_e adapter_le_state,
710 auto adapter = static_cast<BluetoothLEAdapter*>(user_data);
713 LoggerE("user_data is NULL");
717 adapter->enabled_ = ToBool(adapter_le_state);
720 void BluetoothLEAdapter::OnScanResult(
721 int result, bt_adapter_le_device_scan_result_info_s* info,
723 LoggerD("Entered, result: %d, info: %p, data: %p", result, info, user_data);
725 auto adapter = static_cast<BluetoothLEAdapter*>(user_data);
728 LoggerE("user_data is NULL");
732 picojson::value value = picojson::value(picojson::object());
733 picojson::object* data_obj = &value.get<picojson::object>();
735 if (BT_ERROR_NONE != result) {
737 util::GetBluetoothError(result, "Error during scanning"), data_obj,
738 ("Error during scanning: %d (%s)", result, get_error_message(result)));
739 data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanError)));
741 // this is probably capi-network-bluetooth error: when scan is stopped info has 0x1 value
742 if (nullptr != info && reinterpret_cast<void*>(0x1) != info) {
744 LoggerD("Device found");
745 picojson::value data{picojson::object{}};
746 const auto& r = BluetoothLEDevice::ToJson(info, &data.get<picojson::object>());
748 data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanSuccess)));
749 data_obj->insert(std::make_pair(kData, data));
751 LogAndReportError(r, data_obj, ("Failed to parse Bluetooth LE device"));
752 data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanError)));
757 adapter->instance_.FireEvent(kScanEvent, value);
760 void BluetoothLEAdapter::OnAdvertiseResult(
761 int result, bt_advertiser_h advertiser,
762 bt_adapter_le_advertising_state_e adv_state, void* user_data) {
763 LoggerD("Entered, result: %d, advertiser: %p, adv_state: %d, user_data: %p", result, advertiser, adv_state, user_data);
765 auto adapter = static_cast<BluetoothLEAdapter*>(user_data);
768 LoggerE("user_data is NULL");
772 picojson::value value = picojson::value(picojson::object());
773 picojson::object* data_obj = &value.get<picojson::object>();
775 if (BT_ERROR_NONE != result) {
777 util::GetBluetoothError(result, "Error during advertising"), data_obj,
778 ("Error during advertising: %d (%s)", result, get_error_message(result)));
779 data_obj->insert(std::make_pair(kAction, picojson::value(kOnAdvertiseError)));
781 const char* state = (BT_ADAPTER_LE_ADVERTISING_STARTED == adv_state) ? "STARTED" : "STOPPED";
782 LoggerD("Advertise state is: %s", state);
783 data_obj->insert(std::make_pair(kAction, picojson::value(kOnAdvertiseState)));
784 ReportSuccess(picojson::value(state), *data_obj);
787 adapter->instance_.FireEvent(kAdvertiseEvent, value);
790 } // namespace bluetooth
791 } // namespace extension