[BluetoothLE] Implementation of Bluetooth LE adapter.
authorPawel Andruszkiewicz <p.andruszkie@samsung.com>
Mon, 4 May 2015 14:13:27 +0000 (16:13 +0200)
committerPawel Andruszkiewicz <p.andruszkie@samsung.com>
Fri, 8 May 2015 12:28:19 +0000 (21:28 +0900)
Class, startScan(), stopScan().

Change-Id: Ib1b701f6c6208cd767d459a1d322d2462d2674a7
Signed-off-by: Pawel Andruszkiewicz <p.andruszkie@samsung.com>
src/bluetooth/bluetooth.gyp
src/bluetooth/bluetooth_api.js
src/bluetooth/bluetooth_instance.cc
src/bluetooth/bluetooth_instance.h
src/bluetooth/bluetooth_le_adapter.cc [new file with mode: 0644]
src/bluetooth/bluetooth_le_adapter.h [new file with mode: 0644]
src/bluetooth/bluetooth_util.cc
src/bluetooth/bluetooth_util.h

index 4ce4d8d..9bf3975 100644 (file)
@@ -27,6 +27,8 @@
         'bluetooth_health_profile_handler.h',
         'bluetooth_instance.cc',
         'bluetooth_instance.h',
+        'bluetooth_le_adapter.cc',
+        'bluetooth_le_adapter.h',
         'bluetooth_service_handler.cc',
         'bluetooth_service_handler.h',
         'bluetooth_socket.cc',
index e8b84ff..d0d78f7 100644 (file)
@@ -907,24 +907,117 @@ BluetoothHealthChannel.prototype.unsetListener  = function() {
     }
 };
 
+var _bleScanListener = (function() {
+  var kListenerName = 'BluetoothLEScanCallback';
+  var successCallback;
+  var errorCallback;
+  var listenerRegistered = false;
+
+  function callback(event) {
+    var d;
+
+    switch (event.action) {
+      case 'onstarted':
+        break;
+
+      case 'ondevicefound':
+        d = new BluetoothLEDevice(event.data);
+        break;
+
+      case 'onfinished':
+        d = [];
+        event.data.forEach(function(data) {
+          d.push(new BluetoothLEDevice(data));
+        });
+
+        // remove listener
+        removeListener();
+        break;
+
+      case 'onerror':
+        if (errorCallback) {
+          errorCallback(native.getErrorObject(event));
+        }
+        return;
+
+      default:
+        console.log('Unknown mode: ' + event.action);
+        return;
+    }
+
+    if (successCallback && successCallback[event.action]) {
+      successCallback[event.action](d);
+    }
+  }
+
+  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() {
 };
 
 BluetoothLEAdapter.prototype.startScan = function() {
-    console.log('Entered BluetoothLEAdapter.startScan()');
+  console.log('Entered BluetoothLEAdapter.startScan()');
 
-    xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH);
-    //TODO validate
-    //TODO call c++ layer
+  xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH);
+
+  var args = AV.validateMethod(arguments, [{
+    name: 'successCallback',
+    type: AV.Types.LISTENER,
+    values: ['onstarted', 'ondevicefound', 'onfinished']
+  }, {
+    name: 'errorCallback',
+    type: AV.Types.FUNCTION,
+    optional: true,
+    nullable: true
+  }]);
+
+  var result = native.callSync('BluetoothLEAdapter_startScan', {});
+
+  if (native.isFailure(result)) {
+    throw native.getErrorObject(result);
+  }
+
+  _bleScanListener.addListener(args.successCallback, args.errorCallback);
 };
 
 BluetoothLEAdapter.prototype.stopScan = function() {
-    console.log('Entered BluetoothLEAdapter.stopScan()');
+  console.log('Entered BluetoothLEAdapter.stopScan()');
 
-    xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH);
-    //TODO validate
-    //TODO call c++ layer
+  xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH);
+
+  // _bleScanListener.removeListener() is going to be called in 'onfinished' handler
+
+  var result = native.callSync('BluetoothLEAdapter_stopScan', {});
+
+  if (native.isFailure(result)) {
+    _bleScanListener.removeListener();
+    throw native.getErrorObject(result);
+  }
 };
 
 BluetoothLEAdapter.prototype.startAdvertise = function() {
index 7659347..a655d38 100644 (file)
@@ -20,7 +20,8 @@ BluetoothInstance::BluetoothInstance() :
     bluetooth_health_profile_handler_(*this),
     bluetooth_health_application_(bluetooth_health_profile_handler_),
     bluetooth_service_handler_(bluetooth_adapter_),
-    bluetooth_socket_(bluetooth_adapter_)
+    bluetooth_socket_(bluetooth_adapter_),
+    bluetooth_le_adapter_(*this)
 {
   LoggerD("Entered");
   using std::placeholders::_1;
@@ -99,6 +100,12 @@ BluetoothInstance::BluetoothInstance() :
   REGISTER_SYNC("BluetoothSocket_close",
       std::bind(&BluetoothSocket::Close, &bluetooth_socket_, _1, _2));
 
+  // BluetoothLEAdapter
+  REGISTER_SYNC("BluetoothLEAdapter_startScan",
+      std::bind(&BluetoothLEAdapter::StartScan, &bluetooth_le_adapter_, _1, _2));
+  REGISTER_SYNC("BluetoothLEAdapter_stopScan",
+      std::bind(&BluetoothLEAdapter::StopScan, &bluetooth_le_adapter_, _1, _2));
+
   #undef REGISTER_ASYNC
   #undef REGISTER_SYNC
 }
index afbac54..fddb3f6 100644 (file)
@@ -12,6 +12,7 @@
 #include "bluetooth/bluetooth_health_application.h"
 #include "bluetooth/bluetooth_health_channel.h"
 #include "bluetooth/bluetooth_health_profile_handler.h"
+#include "bluetooth/bluetooth_le_adapter.h"
 #include "bluetooth/bluetooth_service_handler.h"
 #include "bluetooth/bluetooth_socket.h"
 #include "bluetooth/bluetooth_util.h"
@@ -40,6 +41,7 @@ class BluetoothInstance: public common::ParsedInstance {
   BluetoothHealthProfileHandler bluetooth_health_profile_handler_;
   BluetoothServiceHandler bluetooth_service_handler_;
   BluetoothSocket bluetooth_socket_;
+  BluetoothLEAdapter bluetooth_le_adapter_;
 };
 
 } // namespace bluetooth
diff --git a/src/bluetooth/bluetooth_le_adapter.cc b/src/bluetooth/bluetooth_le_adapter.cc
new file mode 100644 (file)
index 0000000..426e64c
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "bluetooth/bluetooth_le_adapter.h"
+
+#include "common/logger.h"
+
+#include "bluetooth/bluetooth_instance.h"
+#include "bluetooth/bluetooth_util.h"
+
+namespace extension {
+namespace bluetooth {
+
+namespace {
+
+// TODO: remove this code when BluetoothLEDevice is available
+class BluetoothLEDevice {
+ public:
+  static common::PlatformResult ToJson(
+       bt_adapter_le_device_scan_result_info_s* info,
+       picojson::object* le_device) {
+    return common::PlatformResult(common::ErrorCode::NO_ERROR);
+  }
+};
+// TODO end
+
+// utility functions
+
+bool ToBool(bt_adapter_le_state_e state) {
+  return (BT_ADAPTER_LE_ENABLED == state) ? true : false;
+}
+
+// constants
+
+const std::string kAction = "action";
+const std::string kData = "data";
+// scan-related
+const std::string kOnScanStarted = "onstarted";
+const std::string kOnScanDeviceFound = "ondevicefound";
+const std::string kOnScanFinished = "onfinished";
+const std::string kOnScanError = "onerror";
+const std::string kScanEvent = "BluetoothLEScanCallback";
+
+} // namespace
+
+using common::ErrorCode;
+using common::PlatformResult;
+using common::tools::ReportError;
+using common::tools::ReportSuccess;
+
+BluetoothLEAdapter::BluetoothLEAdapter(BluetoothInstance& instance)
+    : instance_(instance),
+      enabled_(false),
+      scanning_(false) {
+  LoggerD("Entered");
+
+  bt_adapter_le_state_e le_state = BT_ADAPTER_LE_DISABLED;
+
+  int ret = bt_adapter_le_get_state(&le_state);
+
+  if (BT_ERROR_NONE == ret) {
+    enabled_ = ToBool(le_state);
+
+    ret = bt_adapter_le_set_state_changed_cb(OnStateChanged, this);
+
+    if (BT_ERROR_NONE == ret) {
+      if (!enabled_) {
+        LoggerD("BTLE is not enabled, turning on...");
+        // enabled_ is going to be updated by OnStateChanged callback
+        ret = bt_adapter_le_enable();
+
+        if (BT_ERROR_NONE != ret) {
+          LoggerE("Failed to enable BTLE.");
+        }
+      }
+    } else {
+      LoggerE("Failed to register BTLE state changed listener.");
+    }
+  } else {
+    LoggerE("Failed to obtain current state of BTLE.");
+  }
+}
+
+BluetoothLEAdapter::~BluetoothLEAdapter() {
+  bt_adapter_le_unset_state_changed_cb();
+  if (scanning_) {
+    bt_adapter_le_stop_scan();
+  }
+  bt_adapter_le_disable();
+}
+
+void BluetoothLEAdapter::StartScan(const picojson::value& data, picojson::object& out) {
+  LoggerD("Entered");
+
+  int ret = bt_adapter_le_start_scan(OnScanResult, this);
+
+  if (BT_ERROR_NONE != ret) {
+    if (BT_ERROR_NOW_IN_PROGRESS == ret) {
+      LoggerE("Scan in progress");
+      ReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, "Scan already in progress"), &out);
+    } else {
+      LoggerE("Failed to start scan: %d", ret);
+
+      // other errors are reported asynchronously
+      picojson::value value = picojson::value(picojson::object());
+      picojson::object* data_obj = &value.get<picojson::object>();
+      data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanError)));
+      ReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to start scan"), data_obj);
+      instance_.FireEvent(kScanEvent, value);
+    }
+  } else {
+    scanning_ = true;
+    ReportSuccess(out);
+  }
+}
+
+void BluetoothLEAdapter::StopScan(const picojson::value& data, picojson::object& out) {
+  LoggerD("Entered");
+
+  int ret = bt_adapter_le_stop_scan();
+
+  if (BT_ERROR_NONE != ret && BT_ERROR_NOT_IN_PROGRESS != ret) {
+    LoggerE("Failed to stop scan: %d", ret);
+    ReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Failed to stop scan"), &out);
+  } else {
+    scanning_ = false;
+    ReportSuccess(out);
+  }
+}
+
+void BluetoothLEAdapter::StartAdvertise(const picojson::value& data, picojson::object& out) {
+  LoggerD("Entered");
+}
+
+void BluetoothLEAdapter::StopAdvertise(const picojson::value& data, picojson::object& out) {
+  LoggerD("Entered");
+}
+
+void BluetoothLEAdapter::OnStateChanged(int result,
+                                        bt_adapter_le_state_e adapter_le_state,
+                                        void* user_data) {
+  LoggerD("Entered");
+
+  auto adapter = static_cast<BluetoothLEAdapter*>(user_data);
+
+  if (!adapter) {
+    LoggerE("user_data is NULL");
+    return;
+  }
+
+  adapter->enabled_ = ToBool(adapter_le_state);
+}
+
+void BluetoothLEAdapter::OnScanResult(
+    int result, bt_adapter_le_device_scan_result_info_s* info,
+    void* user_data) {
+  LoggerD("Entered, result: %d, info: %p, data: %p", result, info, 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 scanning: %d", result);
+    ReportError(util::GetBluetoothError(result, "Error during scanning"), data_obj);
+    data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanError)));
+  } else {
+    // TODO: this is probably capi-network-bluetooth error: when scan is stopped info has 0x1 value
+    if (nullptr == info || reinterpret_cast<void*>(0x1) == info) {
+      // info is empty, so this is start/stop callback
+      if (!adapter->scanning_) { // scanning has to be stopped by the user, it is not stopped by the platform
+        LoggerD("Scan finished");
+        data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanFinished)));
+        data_obj->insert(std::make_pair(kData, picojson::value(adapter->discovered_devices_)));
+      } else {
+        LoggerD("Scan started");
+        adapter->discovered_devices_.clear();
+        data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanStarted)));
+      }
+    } else {
+      // device found
+      LoggerD("Device found");
+      picojson::value data{picojson::object{}};
+      const auto& r = BluetoothLEDevice::ToJson(info, &data.get<picojson::object>());
+      if (r) {
+        data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanDeviceFound)));
+        data_obj->insert(std::make_pair(kData, data));
+        adapter->discovered_devices_.push_back(data);
+      } else {
+        LoggerE("Failed to parse Bluetooth LE device");
+        ReportError(r, data_obj);
+        data_obj->insert(std::make_pair(kAction, picojson::value(kOnScanError)));
+      }
+    }
+  }
+
+  adapter->instance_.FireEvent(kScanEvent, value);
+}
+
+} // namespace bluetooth
+} // namespace extension
diff --git a/src/bluetooth/bluetooth_le_adapter.h b/src/bluetooth/bluetooth_le_adapter.h
new file mode 100644 (file)
index 0000000..311ef09
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef BLUETOOTH_BLUETOOTH_LE_ADAPTER_H_
+#define BLUETOOTH_BLUETOOTH_LE_ADAPTER_H_
+
+#include <bluetooth.h>
+
+#include "common/picojson.h"
+
+namespace extension {
+namespace bluetooth {
+
+class BluetoothInstance;
+
+class BluetoothLEAdapter {
+ public:
+  explicit BluetoothLEAdapter(BluetoothInstance& instance);
+  ~BluetoothLEAdapter();
+
+  void StartScan(const picojson::value& data, picojson::object& out);
+  void StopScan(const picojson::value& data, picojson::object& out);
+
+  void StartAdvertise(const picojson::value& data, picojson::object& out);
+  void StopAdvertise(const picojson::value& data, picojson::object& out);
+
+ private:
+  BluetoothLEAdapter() = delete;
+  BluetoothLEAdapter(const BluetoothLEAdapter&) = delete;
+  BluetoothLEAdapter(const BluetoothLEAdapter&&) = delete;
+  BluetoothLEAdapter& operator=(const BluetoothLEAdapter&) = delete;
+  BluetoothLEAdapter& operator=(const BluetoothLEAdapter&&) = delete;
+
+  static void OnStateChanged(int result, bt_adapter_le_state_e adapter_le_state,
+                             void* user_data);
+  static void OnScanResult(int result,
+                           bt_adapter_le_device_scan_result_info_s* info,
+                           void* user_data);
+
+  BluetoothInstance& instance_;
+  bool enabled_;
+  bool scanning_;
+  picojson::array discovered_devices_;
+};
+
+} // namespace bluetooth
+} // namespace extension
+
+#endif // BLUETOOTH_BLUETOOTH_LE_ADAPTER_H_
index 78a1618..3b3fbde 100644 (file)
@@ -2,14 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "bluetooth_util.h"
+#include "bluetooth/bluetooth_util.h"
 
-using namespace common;
+#include <bluetooth.h>
 
 namespace extension {
 namespace bluetooth {
 namespace util {
 
+using common::ErrorCode;
+using common::PlatformResult;
+
 namespace {
 const char* JSON_CALLBACK_ID = "callbackId";
 } // namespace
@@ -22,6 +25,89 @@ const picojson::object& GetArguments(const picojson::value& data) {
   return data.get<picojson::object>();
 }
 
+PlatformResult GetBluetoothError(int error_code,
+                                         const std::string& hint) {
+  common::ErrorCode error = ErrorCode::UNKNOWN_ERR;
+
+  switch (error_code) {
+    case BT_ERROR_RESOURCE_BUSY:
+    case BT_ERROR_NOW_IN_PROGRESS:
+    case BT_ERROR_NOT_ENABLED:
+      error = ErrorCode::SERVICE_NOT_AVAILABLE_ERR;
+      break;
+
+    case BT_ERROR_REMOTE_DEVICE_NOT_FOUND:
+      error = ErrorCode::NOT_FOUND_ERR;
+      break;
+
+    case BT_ERROR_INVALID_PARAMETER:
+      error = ErrorCode::INVALID_VALUES_ERR;
+      break;
+
+    case BT_ERROR_QUOTA_EXCEEDED:
+       error = ErrorCode::QUOTA_EXCEEDED_ERR;
+       break;
+
+    default:
+      error = ErrorCode::UNKNOWN_ERR;
+      break;
+  }
+
+  return PlatformResult(error,
+                        hint + " : " + GetBluetoothErrorMessage(error_code));
+}
+
+std::string GetBluetoothErrorMessage(int error_code) {
+  switch (error_code) {
+    case BT_ERROR_CANCELLED:
+      return "Operation cancelled";
+    case BT_ERROR_INVALID_PARAMETER:
+      return "Invalid parameter";
+    case BT_ERROR_OUT_OF_MEMORY:
+      return "Out of memory";
+    case BT_ERROR_RESOURCE_BUSY:
+      return "Bluetooth device is busy";
+    case BT_ERROR_TIMED_OUT:
+      return "Timeout error";
+    case BT_ERROR_NOW_IN_PROGRESS:
+      return "Operation now in progress";
+    case BT_ERROR_NOT_SUPPORTED:
+      return "Not Supported";
+    case BT_ERROR_PERMISSION_DENIED:
+      return "Permission denied";
+    case BT_ERROR_QUOTA_EXCEEDED:
+      return "Quota exceeded";
+    case BT_ERROR_NOT_INITIALIZED:
+      return "Local adapter not initialized";
+    case BT_ERROR_NOT_ENABLED:
+      return "Local adapter not enabled";
+    case BT_ERROR_ALREADY_DONE:
+      return "Operation already done";
+    case BT_ERROR_OPERATION_FAILED:
+      return "Operation failed";
+    case BT_ERROR_NOT_IN_PROGRESS:
+      return "Operation not in progress";
+    case BT_ERROR_REMOTE_DEVICE_NOT_BONDED:
+      return "Remote device not bonded";
+    case BT_ERROR_AUTH_REJECTED:
+      return "Authentication rejected";
+    case BT_ERROR_AUTH_FAILED:
+      return "Authentication failed";
+    case BT_ERROR_REMOTE_DEVICE_NOT_FOUND:
+      return "Remote device not found";
+    case BT_ERROR_SERVICE_SEARCH_FAILED:
+      return "Service search failed";
+    case BT_ERROR_REMOTE_DEVICE_NOT_CONNECTED:
+      return "Remote device is not connected";
+    case BT_ERROR_AGAIN:
+      return "Resource temporarily unavailable";
+    case BT_ERROR_SERVICE_NOT_FOUND:
+      return "Service Not Found";
+    default:
+      return "Unknown Error";
+  }
+}
+
 } // util
 } // bluetooth
 } // extension
index 198be84..fb06f62 100644 (file)
@@ -19,6 +19,9 @@ double GetAsyncCallbackHandle(const picojson::value& data);
 
 const picojson::object& GetArguments(const picojson::value& data);
 
+common::PlatformResult GetBluetoothError(int error_code, const std::string& hint);
+std::string GetBluetoothErrorMessage(int error_code);
+
 } // util
 } // bluetooth
 } // extension