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