Class, startScan(), stopScan().
Change-Id: Ib1b701f6c6208cd767d459a1d322d2462d2674a7
Signed-off-by: Pawel Andruszkiewicz <p.andruszkie@samsung.com>
'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',
}
};
+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() {
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;
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
}
#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"
BluetoothHealthProfileHandler bluetooth_health_profile_handler_;
BluetoothServiceHandler bluetooth_service_handler_;
BluetoothSocket bluetooth_socket_;
+ BluetoothLEAdapter bluetooth_le_adapter_;
};
} // namespace bluetooth
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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_
// 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
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
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