[BluetoothLE] Advertising in bluetooth LE adapter implemented.
authorPawel Andruszkiewicz <p.andruszkie@samsung.com>
Tue, 5 May 2015 12:23:25 +0000 (14:23 +0200)
committerPawel Andruszkiewicz <p.andruszkie@samsung.com>
Fri, 8 May 2015 12:35:46 +0000 (21:35 +0900)
Change-Id: Iae24a17f840a26cd898a43140d7c25c73d5785f8
Signed-off-by: Pawel Andruszkiewicz <p.andruszkie@samsung.com>
src/bluetooth/bluetooth_api.js
src/bluetooth/bluetooth_instance.cc
src/bluetooth/bluetooth_le_adapter.cc
src/bluetooth/bluetooth_le_adapter.h

index 3afd0e1e63042ecabce4436fdc8b776cd4e0cf93..12d109279c5deb7e6b3dc73d7590f8cd61be3985 100644 (file)
@@ -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 ////////////////////////////////////////////////////
index 9781c67bc322b50ac1e3c8c288e983dfce8c7d66..7d770434fc1e42ac9e478ca7284e6fe1cf03c521 100644 (file)
@@ -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(
index 718e402824bd40cda89fe7b77003fc1e5a4c6996..d51ac3d8ae625768b7cb357d7eac544d8883b14e 100644 (file)
@@ -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<picojson::object>() ||
+        !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<std::string>()) {
+      out->uuid_ = uuid.get<std::string>();
+    } else {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool ParseData(const picojson::value& obj,
+                        BluetoothLEServiceData* out) {
+    const auto& data = obj.get("data");
+    if (data.is<std::string>()) {
+      out->data_ = data.get<std::string>();
+    } 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<picojson::object>() ||
+        !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<std::string>()) {
+      try {
+        out->id_ = std::stoi(id.get<std::string>());
+      } catch (...) {
+        LoggerE("Failed to convert string to int: %s", id.get<std::string>().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<std::string>()) {
+      out->data_ = data.get<std::string>();
+    } 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<std::string>& service_uuids() const {
+    return service_uuids_;
+  }
+
+  const std::vector<std::string>& 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<BluetoothLEServiceData>& 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<picojson::object>() ||
+        !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<bool>()) {
+      out->include_name_ = include_name.get<bool>();
+    } else if (!include_name.is<picojson::null>()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool ParseServiceUUIDs(const picojson::value& obj,
+                                BluetoothLEAdvertiseData* out) {
+    const auto& service_uuids = obj.get("serviceuuids");
+    if (service_uuids.is<picojson::array>()) {
+      for (const auto& i : service_uuids.get<picojson::array>()) {
+        if (i.is<std::string>()) {
+          out->service_uuids_.push_back(i.get<std::string>());
+        } else {
+          return false;
+        }
+      }
+    } else if (!service_uuids.is<picojson::null>()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool ParseSolicitationUUIDs(const picojson::value& obj,
+                                     BluetoothLEAdvertiseData* out) {
+    const auto& solicitation_uuids = obj.get("solicitationuuids");
+    if (solicitation_uuids.is<picojson::array>()) {
+      for (const auto& i : solicitation_uuids.get<picojson::array>()) {
+        if (i.is<std::string>()) {
+          out->solicitation_uuids_.push_back(i.get<std::string>());
+        } else {
+          return false;
+        }
+      }
+    } else if (!solicitation_uuids.is<picojson::null>()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool ParseAppearance(const picojson::value& obj,
+                              BluetoothLEAdvertiseData* out) {
+    const auto& appearance = obj.get("appearance");
+    if (appearance.is<double>()) {
+      out->appearance_ = static_cast<decltype(appearance_)>(appearance.get<double>());
+    } else if (!appearance.is<picojson::null>()) {
+      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<bool>()) {
+      out->include_tx_power_level_ = include_tx_power_level.get<bool>();
+    } else if (!include_tx_power_level.is<picojson::null>()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  static bool ParseServiceData(const picojson::value& obj,
+                               BluetoothLEAdvertiseData* out) {
+    const auto& service_data = obj.get("serviceData");
+    if (service_data.is<picojson::array>()) {
+      for (const auto& i : service_data.get<picojson::array>()) {
+        BluetoothLEServiceData data;
+        if (BluetoothLEServiceData::Construct(i, &data)) {
+          out->service_data_.push_back(std::move(data));
+        } else {
+          return false;
+        }
+      }
+    } else if (!service_data.is<picojson::null>()) {
+      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<picojson::null>()) {
+      return false;
+    }
+
+    return true;
+  }
+
+  bool include_name_;
+  std::vector<std::string> service_uuids_;
+  std::vector<std::string> solicitation_uuids_;
+  int appearance_;
+  bool include_tx_power_level_;
+  std::vector<BluetoothLEServiceData> 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<picojson::object>() ||
+      !json_packet_type.is<std::string>() ||
+      !json_mode.is<std::string>() ||
+      !json_connectable.is<bool>()) {
+    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<std::string>();
+    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<std::string>();
+    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<std::remove_pointer<bt_advertiser_h>::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<bool>());
+  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<BluetoothLEAdapter*>(user_data);
+
+  if (!adapter) {
+    LoggerE("user_data is NULL");
+    return;
+  }
+
+  picojson::value value = picojson::value(picojson::object());
+  picojson::object* data_obj = &value.get<picojson::object>();
+
+  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
index 311ef096b701d133efb8be9387e56846ee854f21..31d6f041308b2d49368009a8b85914ea373435d3 100644 (file)
@@ -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