From: Pawel Andruszkiewicz Date: Tue, 5 May 2015 12:23:25 +0000 (+0200) Subject: [BluetoothLE] Advertising in bluetooth LE adapter implemented. X-Git-Tag: submit/tizen_tv/20150603.064601~1^2~67 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=35adc7b5b503dace68bd5809e0c51da0e6e38af8;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [BluetoothLE] Advertising in bluetooth LE adapter implemented. Change-Id: Iae24a17f840a26cd898a43140d7c25c73d5785f8 Signed-off-by: Pawel Andruszkiewicz --- diff --git a/src/bluetooth/bluetooth_api.js b/src/bluetooth/bluetooth_api.js index 3afd0e1e..12d10927 100644 --- a/src/bluetooth/bluetooth_api.js +++ b/src/bluetooth/bluetooth_api.js @@ -135,7 +135,7 @@ var BluetoothClassDeviceService = function() { //class BluetoothLEServiceData //////////////////////////////////////////////////// var BluetoothLEServiceData = function(data) { Object.defineProperties(this, { - serviceuuids : {value: data.serviceData, writable: true, enumerable: true}, + serviceuuid : {value: data.serviceData, writable: true, enumerable: true}, data : {value: data.data, writable: true, enumerable: true} }); }; @@ -143,11 +143,11 @@ var BluetoothLEServiceData = function(data) { //class BluetoothLEAdvertiseData //////////////////////////////////////////////////// var BluetoothLEAdvertiseData = function(data) { Object.defineProperties(this, { - name : {value: false, writable: true, enumerable: true}, + includeName : {value: false, writable: true, enumerable: true}, serviceuuids : {value: ["dummyUuids"], writable: true, enumerable: true}, solicitationuuids : {value: ["dummySolicitationuuids"], writable: true, enumerable: true}, appearance : {value: 123456, writable: true, enumerable: true}, - txpowerLevel : {value: false, writable: true, enumerable: true}, + includeTxPowerLevel : {value: false, writable: true, enumerable: true}, serviceData : {value: ["dummyServiceData"], writable: true, enumerable: true}, manufacturerData : {value: null, writable: true, enumerable: true} }); @@ -1008,6 +1008,60 @@ var _bleScanListener = (function() { }; })(); +var _bleAdvertiseListener = (function() { + var kListenerName = 'BluetoothLEAdvertiseCallback'; + var successCallback; + var errorCallback; + var listenerRegistered = false; + + function callback(event) { + var d; + + switch (event.action) { + case 'onstate': + if (successCallback) { + successCallback(native.getResultObject(event)); + } + return; + + case 'onerror': + if (errorCallback) { + errorCallback(native.getErrorObject(event)); + } + return; + + default: + console.log('Unknown mode: ' + event.action); + return; + } + } + + function addListener(s, e) { + successCallback = s; + errorCallback = e; + + if (!listenerRegistered) { + native.addListener(kListenerName, callback); + listenerRegistered = true; + } + } + + function removeListener() { + if (listenerRegistered) { + native.removeListener(kListenerName, callback); + listenerRegistered = false; + } + + successCallback = undefined; + errorCallback = undefined; + } + + return { + addListener: addListener, + removeListener: removeListener + }; +})(); + //class BluetoothLEAdapter //////////////////////////////////////////////////// var BluetoothLEAdapter = function() { }; @@ -1052,20 +1106,80 @@ BluetoothLEAdapter.prototype.stopScan = function() { } }; +var _BluetoothAdvertisePacketType = { + ADVERTISE: 'ADVERTISE', + SCAN_RESPONSE: 'SCAN_RESPONSE' +}; + +var _BluetoothAdvertisingMode = { + BALANCED: 'BALANCED', + LOW_LATENCY: 'LOW_LATENCY', + LOW_ENERGY: 'LOW_ENERGY' +}; + BluetoothLEAdapter.prototype.startAdvertise = function() { - console.log('Entered BluetoothLEAdapter.startAdvertise()'); + console.log('Entered BluetoothLEAdapter.startAdvertise()'); - xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH); - //TODO validate - //TODO call c++ layer + xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH); + + var args = AV.validateMethod(arguments, [{ + name: 'advertiseData', + type: AV.Types.PLATFORM_OBJECT, + values: BluetoothLEAdvertiseData + }, { + name: 'packetType', + type: AV.Types.ENUM, + values: T.getValues(_BluetoothAdvertisePacketType) + }, { + name: 'successCallback', + type: AV.Types.FUNCTION + }, { + name: 'errorCallback', + type: AV.Types.FUNCTION, + optional: true, + nullable: true + }, { + name: 'mode', + type: AV.Types.ENUM, + values: T.getValues(_BluetoothAdvertisingMode), + optional: true, + nullable: true + }, { + name: 'connectable', + type: AV.Types.BOOLEAN, + optional: true, + nullable: true + }]); + + var callArgs = { + advertiseData: args.advertiseData, + packetType: args.packetType, + mode: T.isNullOrUndefined(args.mode) ? _BluetoothAdvertisingMode.BALANCED : args.mode, + connectable: T.isNullOrUndefined(args.connectable) ? true : args.connectable + }; + + var result = native.callSync('BluetoothLEAdapter_startAdvertise', callArgs); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } + + _bleAdvertiseListener.addListener(args.successCallback, args.errorCallback); }; BluetoothLEAdapter.prototype.stopAdvertise = function() { - console.log('Entered BluetoothLEAdapter.stopAdvertise()'); + console.log('Entered BluetoothLEAdapter.stopAdvertise()'); - xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH); - //TODO validate - //TODO call c++ layer + xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH); + + // TODO: when should we call _bleAdvertiseListener.removeListener()? + + var result = native.callSync('BluetoothLEAdapter_stopAdvertise', {}); + + if (native.isFailure(result)) { + _bleAdvertiseListener.removeListener(); + throw native.getErrorObject(result); + } }; //class BluetoothGATTService //////////////////////////////////////////////////// diff --git a/src/bluetooth/bluetooth_instance.cc b/src/bluetooth/bluetooth_instance.cc index 9781c67b..7d770434 100644 --- a/src/bluetooth/bluetooth_instance.cc +++ b/src/bluetooth/bluetooth_instance.cc @@ -106,6 +106,10 @@ BluetoothInstance::BluetoothInstance() : std::bind(&BluetoothLEAdapter::StartScan, &bluetooth_le_adapter_, _1, _2)); REGISTER_SYNC("BluetoothLEAdapter_stopScan", std::bind(&BluetoothLEAdapter::StopScan, &bluetooth_le_adapter_, _1, _2)); + REGISTER_SYNC("BluetoothLEAdapter_startAdvertise", + std::bind(&BluetoothLEAdapter::StartAdvertise, &bluetooth_le_adapter_, _1, _2)); + REGISTER_SYNC("BluetoothLEAdapter_stopAdvertise", + std::bind(&BluetoothLEAdapter::StopAdvertise, &bluetooth_le_adapter_, _1, _2)); // BluetoothLEDevice REGISTER_ASYNC( diff --git a/src/bluetooth/bluetooth_le_adapter.cc b/src/bluetooth/bluetooth_le_adapter.cc index 718e4028..d51ac3d8 100644 --- a/src/bluetooth/bluetooth_le_adapter.cc +++ b/src/bluetooth/bluetooth_le_adapter.cc @@ -27,6 +27,311 @@ namespace bluetooth { namespace { +class ParsedDataHolder { + public: + ParsedDataHolder() : valid_(false) {} + virtual ~ParsedDataHolder() {} + + bool valid() const { + return valid_; + } + + protected: + void set_valid() { + valid_ = true; + } + + private: + bool valid_; +}; + +class BluetoothLEServiceData : public ParsedDataHolder { + public: + BluetoothLEServiceData() + : ParsedDataHolder() { + } + + const std::string& uuid() const { + return uuid_; + } + + const std::string& data() const { + return data_; + } + + static bool Construct(const picojson::value& obj, + BluetoothLEServiceData* out) { + if (!obj.is() || + !ParseUUID(obj, out) || + !ParseData(obj, out)) { + return false; + } + + out->set_valid(); + + return true; + } + + private: + static bool ParseUUID(const picojson::value& obj, + BluetoothLEServiceData* out) { + const auto& uuid = obj.get("serviceuuid"); + if (uuid.is()) { + out->uuid_ = uuid.get(); + } else { + return false; + } + + return true; + } + + static bool ParseData(const picojson::value& obj, + BluetoothLEServiceData* out) { + const auto& data = obj.get("data"); + if (data.is()) { + out->data_ = data.get(); + } else { + return false; + } + + return true; + } + + std::string uuid_; + std::string data_; +}; + +class BluetoothLEManufacturerData : public ParsedDataHolder { + public: + BluetoothLEManufacturerData() + : ParsedDataHolder(), + id_(-1) { + } + + int id() const { + return id_; + } + + const std::string& data() const { + return data_; + } + + static bool Construct(const picojson::value& obj, + BluetoothLEManufacturerData* out) { + if (!obj.is() || + !ParseId(obj, out) || + !ParseData(obj, out)) { + return false; + } + + out->set_valid(); + + return true; + } + + private: + static bool ParseId(const picojson::value& obj, + BluetoothLEManufacturerData* out) { + const auto& id = obj.get("id"); + if (id.is()) { + try { + out->id_ = std::stoi(id.get()); + } catch (...) { + LoggerE("Failed to convert string to int: %s", id.get().c_str()); + return false; + } + } else { + return false; + } + + return true; + } + + static bool ParseData(const picojson::value& obj, + BluetoothLEManufacturerData* out) { + const auto& data = obj.get("data"); + if (data.is()) { + out->data_ = data.get(); + } else { + return false; + } + + return true; + } + + int id_; + std::string data_; +}; + +class BluetoothLEAdvertiseData : public ParsedDataHolder { + public: + BluetoothLEAdvertiseData() + : ParsedDataHolder(), + include_name_(false), + appearance_(0), // 0 means unknown + include_tx_power_level_(false) { + } + + bool include_name() const { + return include_name_; + } + + const std::vector& service_uuids() const { + return service_uuids_; + } + + const std::vector& solicitation_uuids() const { + return solicitation_uuids_; + } + + int appearance() const { + return appearance_; + } + + bool include_tx_power_level() const { + return include_tx_power_level_; + } + + const std::vector& service_data() const { + return service_data_; + } + + const BluetoothLEManufacturerData& manufacturer_data() const { + return manufacturer_data_; + } + + static bool Construct(const picojson::value& obj, + BluetoothLEAdvertiseData* out) { + if (!obj.is() || + !ParseIncludeName(obj, out) || + !ParseServiceUUIDs(obj, out) || + !ParseSolicitationUUIDs(obj, out) || + !ParseAppearance(obj, out) || + !ParseIncludeTxPowerLevel(obj, out) || + !ParseServiceData(obj, out) || + !ParseManufacturerData(obj, out)) { + return false; + } + + out->set_valid(); + + return true; + } + + private: + static bool ParseIncludeName(const picojson::value& obj, + BluetoothLEAdvertiseData* out) { + const auto& include_name = obj.get("includeName"); + if (include_name.is()) { + out->include_name_ = include_name.get(); + } else if (!include_name.is()) { + return false; + } + + return true; + } + + static bool ParseServiceUUIDs(const picojson::value& obj, + BluetoothLEAdvertiseData* out) { + const auto& service_uuids = obj.get("serviceuuids"); + if (service_uuids.is()) { + for (const auto& i : service_uuids.get()) { + if (i.is()) { + out->service_uuids_.push_back(i.get()); + } else { + return false; + } + } + } else if (!service_uuids.is()) { + return false; + } + + return true; + } + + static bool ParseSolicitationUUIDs(const picojson::value& obj, + BluetoothLEAdvertiseData* out) { + const auto& solicitation_uuids = obj.get("solicitationuuids"); + if (solicitation_uuids.is()) { + for (const auto& i : solicitation_uuids.get()) { + if (i.is()) { + out->solicitation_uuids_.push_back(i.get()); + } else { + return false; + } + } + } else if (!solicitation_uuids.is()) { + return false; + } + + return true; + } + + static bool ParseAppearance(const picojson::value& obj, + BluetoothLEAdvertiseData* out) { + const auto& appearance = obj.get("appearance"); + if (appearance.is()) { + out->appearance_ = static_cast(appearance.get()); + } else if (!appearance.is()) { + return false; + } + + return true; + } + + static bool ParseIncludeTxPowerLevel(const picojson::value& obj, + BluetoothLEAdvertiseData* out) { + const auto& include_tx_power_level = obj.get("includeTxPowerLevel"); + if (include_tx_power_level.is()) { + out->include_tx_power_level_ = include_tx_power_level.get(); + } else if (!include_tx_power_level.is()) { + return false; + } + + return true; + } + + static bool ParseServiceData(const picojson::value& obj, + BluetoothLEAdvertiseData* out) { + const auto& service_data = obj.get("serviceData"); + if (service_data.is()) { + for (const auto& i : service_data.get()) { + BluetoothLEServiceData data; + if (BluetoothLEServiceData::Construct(i, &data)) { + out->service_data_.push_back(std::move(data)); + } else { + return false; + } + } + } else if (!service_data.is()) { + return false; + } + + return true; + } + + static bool ParseManufacturerData(const picojson::value& obj, + BluetoothLEAdvertiseData* out) { + const auto& manufacturer_data = obj.get("manufacturerData"); + BluetoothLEManufacturerData data; + if (BluetoothLEManufacturerData::Construct(manufacturer_data, &data)) { + out->manufacturer_data_ = std::move(data); + } else if (!manufacturer_data.is()) { + return false; + } + + return true; + } + + bool include_name_; + std::vector service_uuids_; + std::vector solicitation_uuids_; + int appearance_; + bool include_tx_power_level_; + std::vector service_data_; + BluetoothLEManufacturerData manufacturer_data_; +}; + // utility functions bool ToBool(bt_adapter_le_state_e state) { @@ -43,6 +348,10 @@ const std::string kOnScanDeviceFound = "ondevicefound"; const std::string kOnScanFinished = "onfinished"; const std::string kOnScanError = "onerror"; const std::string kScanEvent = "BluetoothLEScanCallback"; +// advertise-related +const std::string kOnAdvertiseState = "onstate"; +const std::string kOnAdvertiseError = "onerror"; +const std::string kAdvertiseEvent = "BluetoothLEAdvertiseCallback"; } // namespace @@ -54,7 +363,8 @@ using common::tools::ReportSuccess; BluetoothLEAdapter::BluetoothLEAdapter(BluetoothInstance& instance) : instance_(instance), enabled_(false), - scanning_(false) { + scanning_(false), + bt_advertiser_(nullptr) { LoggerD("Entered"); bt_adapter_le_state_e le_state = BT_ADAPTER_LE_DISABLED; @@ -89,6 +399,10 @@ BluetoothLEAdapter::~BluetoothLEAdapter() { if (scanning_) { bt_adapter_le_stop_scan(); } + if (bt_advertiser_) { + bt_adapter_le_stop_advertising(bt_advertiser_); + bt_adapter_le_destroy_advertiser(bt_advertiser_); + } bt_adapter_le_disable(); } @@ -133,10 +447,201 @@ void BluetoothLEAdapter::StopScan(const picojson::value& data, picojson::object& void BluetoothLEAdapter::StartAdvertise(const picojson::value& data, picojson::object& out) { LoggerD("Entered"); + + const auto& json_advertise_data = data.get("advertiseData"); + const auto& json_packet_type = data.get("packetType"); + const auto& json_mode = data.get("mode"); + const auto& json_connectable = data.get("connectable"); + + if (!json_advertise_data.is() || + !json_packet_type.is() || + !json_mode.is() || + !json_connectable.is()) { + ReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Unexpected parameter type"), &out); + return; + } + + BluetoothLEAdvertiseData advertise_data; + if (!BluetoothLEAdvertiseData::Construct(json_advertise_data, &advertise_data)) { + ReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Unexpected value of advertise data"), &out); + return; + } + + bt_adapter_le_packet_type_e packet_type = BT_ADAPTER_LE_PACKET_ADVERTISING; + { + const auto& str_packet_type = json_packet_type.get(); + if ("ADVERTISE" == str_packet_type) { + packet_type = BT_ADAPTER_LE_PACKET_ADVERTISING; + } else if ("SCAN_RESPONSE" == str_packet_type) { + packet_type = BT_ADAPTER_LE_PACKET_SCAN_RESPONSE; + } else { + ReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Unexpected value of packet type"), &out); + return; + } + } + + bt_adapter_le_advertising_mode_e mode = BT_ADAPTER_LE_ADVERTISING_MODE_BALANCED; + { + const auto& str_mode = json_mode.get(); + if ("BALANCED" == str_mode) { + mode = BT_ADAPTER_LE_ADVERTISING_MODE_BALANCED; + } else if ("LOW_LATENCY" == str_mode) { + mode = BT_ADAPTER_LE_ADVERTISING_MODE_LOW_LATENCY; + } else if ("LOW_ENERGY" == str_mode) { + mode = BT_ADAPTER_LE_ADVERTISING_MODE_LOW_ENERGY; + } else { + ReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, "Unexpected value of mode"), &out); + return; + } + } + + if (nullptr != bt_advertiser_) { + LoggerE("Advertise in progress"); + ReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Advertise already in progress"), &out); + return; + } + + bt_advertiser_h advertiser = nullptr; + + int ret = bt_adapter_le_create_advertiser(&advertiser); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_create_advertiser() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + + std::unique_ptr::type, + int (*)(bt_advertiser_h)> advertiser_ptr(advertiser, &bt_adapter_le_destroy_advertiser); // automatically release the memory + + // configure advertiser + + ret = bt_adapter_le_set_advertising_device_name(advertiser, packet_type, + advertise_data.include_name()); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_set_advertising_device_name() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + + for (const auto& i : advertise_data.service_uuids()) { + ret = bt_adapter_le_add_advertising_service_uuid(advertiser, packet_type, + i.c_str()); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_add_advertising_service_uuid() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + } + + for (const auto& i : advertise_data.solicitation_uuids()) { + ret = bt_adapter_le_add_advertising_service_solicitation_uuid(advertiser, + packet_type, + i.c_str()); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_add_advertising_service_solicitation_uuid() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + } + + ret = bt_adapter_le_set_advertising_appearance(advertiser, packet_type, + advertise_data.appearance()); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_set_advertising_appearance() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + + ret = bt_adapter_le_set_advertising_tx_power_level(advertiser, packet_type, + advertise_data.include_tx_power_level()); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_set_advertising_tx_power_level() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + + for (const auto& i : advertise_data.service_data()) { + ret = bt_adapter_le_add_advertising_service_data(advertiser, packet_type, + i.uuid().c_str(), + i.data().c_str(), + i.data().length()); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_add_advertising_service_data() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + } + + const auto& manufacturer_data = advertise_data.manufacturer_data(); + if (manufacturer_data.valid()) { + ret = bt_adapter_le_add_advertising_manufacturer_data(advertiser, + packet_type, + manufacturer_data.id(), + manufacturer_data.data().c_str(), + manufacturer_data.data().length()); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_add_advertising_manufacturer_data() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + } + + ret = bt_adapter_le_set_advertising_mode(advertiser, mode); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_set_advertising_mode() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + + ret = bt_adapter_le_set_advertising_connectable(advertiser, json_connectable.get()); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_adapter_le_set_advertising_connectable() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to create advertiser"), &out); + return; + } + + // advertiser is ready, let's start advertising + ret = bt_adapter_le_start_advertising_new(advertiser, OnAdvertiseResult, this); + if (BT_ERROR_NONE != ret) { + if (BT_ERROR_NOW_IN_PROGRESS == ret) { + ReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Advertise already in progress"), &out); + return; + } + + LoggerE("bt_adapter_le_start_advertising_new() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to start advertising"), &out); + return; + } + + // everything went well, we want to store the pointer, so unique_ptr should no longer manage the memory + bt_advertiser_ = advertiser_ptr.release(); + ReportSuccess(out); } void BluetoothLEAdapter::StopAdvertise(const picojson::value& data, picojson::object& out) { LoggerD("Entered"); + + if (nullptr != bt_advertiser_) { + int ret = bt_adapter_le_stop_advertising(bt_advertiser_); + if (BT_ERROR_NONE != ret && BT_ERROR_NOT_IN_PROGRESS != ret) { + LoggerE("bt_adapter_le_stop_advertising() failed with: %d", ret); + ReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to stop advertising"), &out); + return; + } + + ret = bt_adapter_le_destroy_advertiser(bt_advertiser_); + if (BT_ERROR_NONE != ret && BT_ERROR_NOT_IN_PROGRESS != ret) { + LoggerE("bt_adapter_le_destroy_advertiser() failed with: %d", ret); + ReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to destroy advertiser"), &out); + return; + } + + bt_advertiser_ = nullptr; + } else { + LoggerD("Advertising is not in progress"); + } + + ReportSuccess(out); } void BluetoothLEAdapter::OnStateChanged(int result, @@ -206,5 +711,34 @@ void BluetoothLEAdapter::OnScanResult( adapter->instance_.FireEvent(kScanEvent, value); } +void BluetoothLEAdapter::OnAdvertiseResult( + int result, bt_advertiser_h advertiser, + bt_adapter_le_advertising_state_e adv_state, void* user_data) { + LoggerD("Entered, result: %d, advertiser: %p, adv_state: %d, user_data: %p", result, advertiser, adv_state, user_data); + + auto adapter = static_cast(user_data); + + if (!adapter) { + LoggerE("user_data is NULL"); + return; + } + + picojson::value value = picojson::value(picojson::object()); + picojson::object* data_obj = &value.get(); + + if (BT_ERROR_NONE != result) { + LoggerE("Error during advertising: %d", result); + ReportError(util::GetBluetoothError(result, "Error during advertising"), data_obj); + data_obj->insert(std::make_pair(kAction, picojson::value(kOnAdvertiseError))); + } else { + const char* state = (BT_ADAPTER_LE_ADVERTISING_STARTED == adv_state) ? "STARTED" : "STOPPED"; + LoggerD("Advertise state is: %s", state); + data_obj->insert(std::make_pair(kAction, picojson::value(kOnAdvertiseState))); + ReportSuccess(picojson::value(state), *data_obj); + } + + adapter->instance_.FireEvent(kAdvertiseEvent, value); +} + } // namespace bluetooth } // namespace extension diff --git a/src/bluetooth/bluetooth_le_adapter.h b/src/bluetooth/bluetooth_le_adapter.h index 311ef096..31d6f041 100644 --- a/src/bluetooth/bluetooth_le_adapter.h +++ b/src/bluetooth/bluetooth_le_adapter.h @@ -49,11 +49,15 @@ class BluetoothLEAdapter { static void OnScanResult(int result, bt_adapter_le_device_scan_result_info_s* info, void* user_data); + static void OnAdvertiseResult(int result, bt_advertiser_h advertiser, + bt_adapter_le_advertising_state_e adv_state, + void* user_data); BluetoothInstance& instance_; bool enabled_; bool scanning_; picojson::array discovered_devices_; + bt_advertiser_h bt_advertiser_; }; } // namespace bluetooth