From: Pawel Andruszkiewicz Date: Mon, 2 Feb 2015 17:21:33 +0000 (+0100) Subject: [Bluetooth] Ported old bluetooth code. X-Git-Tag: submit/tizen_tv/20150603.064601~1^2~486 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=0ac1006e0d1a49f1fc438774bc858d7591d0b46b;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Bluetooth] Ported old bluetooth code. Change-Id: I63a940295b3d80dffcadac60ef70e0cead3ffbdc Signed-off-by: Pawel Andruszkiewicz --- diff --git a/src/bluetooth/bluetooth.gyp b/src/bluetooth/bluetooth.gyp new file mode 100644 index 00000000..4b6dea9d --- /dev/null +++ b/src/bluetooth/bluetooth.gyp @@ -0,0 +1,51 @@ +{ + 'includes':[ + '../common/common.gypi', + ], + 'targets': [ + { + 'target_name': 'tizen_bluetooth', + 'type': 'loadable_module', + 'sources': [ + 'bluetooth_api.js', + 'bluetooth_adapter.cc', + 'bluetooth_adapter.h', + 'bluetooth_class.cc', + 'bluetooth_class.h', + 'bluetooth_device.cc', + 'bluetooth_device.h', + 'bluetooth_extension.cc', + 'bluetooth_extension.h', + 'bluetooth_health_application.cc', + 'bluetooth_health_application.h', + 'bluetooth_health_channel.cc', + 'bluetooth_health_channel.h', + 'bluetooth_health_profile_handler.cc', + 'bluetooth_health_profile_handler.h', + 'bluetooth_instance.cc', + 'bluetooth_instance.h', + 'bluetooth_privilege.h', + 'bluetooth_service_handler.cc', + 'bluetooth_service_handler.h', + 'bluetooth_socket.cc', + 'bluetooth_socket.h', + 'bluetooth_util.cc', + 'bluetooth_util.h', + ], + 'includes': [ + '../common/pkg-config.gypi', + ], + 'conditions': [ + ['tizen == 1', { + 'variables': { + 'packages': [ + 'capi-network-bluetooth', + 'capi-system-info', + 'libpcrecpp', + ] + }, + }], + ], + }, + ], +} diff --git a/src/bluetooth/bluetooth_adapter.cc b/src/bluetooth/bluetooth_adapter.cc new file mode 100644 index 00000000..9a90197f --- /dev/null +++ b/src/bluetooth/bluetooth_adapter.cc @@ -0,0 +1,1538 @@ +/* + * 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_adapter.h" + +#include +#include + +#include +#include +#include + +#include "common/converter.h" +#include "common/logger.h" +#include "common/platform_exception.h" +#include "common/task-queue.h" + +#include "bluetooth_class.h" +#include "bluetooth_device.h" +#include "bluetooth_privilege.h" +#include "bluetooth_socket.h" +#include "bluetooth_util.h" + +namespace extension { +namespace bluetooth { + +using namespace common; + +namespace { +const std::string kAction = "action"; +const std::string kData = "data"; +const std::string kName = "name"; +//adapter +const std::string kAdapterPowered = "powered"; +const std::string kAdapterVisible = "visible"; +//AdapterChangeCallback +const std::string kOnStateChanged = "onstatechanged"; +const std::string kOnNameChanged = "onnamechanged"; +const std::string kOnVisibilityChanged = "onvisibilitychanged"; +const std::string kAdapterChangeCallbackEvent = "BluetoothAdapterChangeCallback"; +// BluetoothProfileHandler +const std::string kBluetoothProfileHealth = "HEALTH"; +const std::string kFeatureBluetoothHealth = "tizen.org/feature/network.bluetooth.health"; +//DiscoverDevicesCallback +const std::string kOnDiscoverStarted = "onstarted"; +const std::string kOnDiscoverFound = "ondevicefound"; +const std::string kOnDiscoverDisappeared = "ondevicedisappeared"; +const std::string kOnDiscoverFinished = "onfinished"; +const std::string kAdapterDiscoverSuccessEvent = "BluetoothDiscoverDevicesSuccessCallback"; +const std::string kAdapterDiscoverErrorEvent = "BluetoothDiscoverDevicesErrorCallback"; +//device +const std::string kDeviceAddress = "address"; +const unsigned short kTimeout = 180; +} + +static bool IsValidAddress(const std::string& address) { + static pcrecpp::RE re("(([0-9a-zA-Z]+):)+([0-9a-zA-Z]+)"); + static std::string compare_address = "00:12:47:08:9A:A6"; + + if (!re.FullMatch(address)) { + LoggerE("Invalid address"); + return false; + } + if (address.size() != compare_address.size()) { + LoggerE("Invalid size"); + return false; + } + return true; +} + +static bool IsValidUUID(const std::string& uuid) { + static pcrecpp::RE re("(([0-9a-zA-Z]+)-)+([0-9a-zA-Z]+)"); + static std::string compare_uuid = "00001101-0000-1000-8000-00805F9B34FB"; + + if (!re.FullMatch(uuid)) { + LoggerE("Invalid UUID: %s", uuid.c_str()); + return false; + } + + if (uuid.size() != compare_uuid.size()) { + LoggerE("Invalid size: %s", uuid.c_str()); + return false; + } + + return true; +} + +void BluetoothAdapter::StateChangedCB(int result, bt_adapter_state_e state, void *user_data) { + LoggerD("Entered"); + + BluetoothAdapter* adapter = static_cast(user_data); + if (!adapter) { + LoggerD("User data is NULL"); + return; + } + + const bool powered = BT_ADAPTER_ENABLED == state; + bool previous_powered = adapter->is_powered_; + adapter->is_powered_ = powered; + + if (powered) { + //update visible state if bluetooth device has been turned on + adapter->is_visible_ = adapter->get_visible(); + } + + if (previous_powered != powered && BT_ERROR_NONE == result) { + picojson::value value = picojson::value(picojson::object()); + picojson::object* data_obj = &value.get(); + + data_obj->insert(std::make_pair(kAction, picojson::value(kOnStateChanged))); + data_obj->insert(std::make_pair(kAdapterPowered, picojson::value(powered))); + + util::FireEvent(kAdapterChangeCallbackEvent, value); + } + + if (adapter->user_request_list_[SET_POWERED]) { + if (adapter->requested_powered_ != powered) { + return; + } + + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + try { + switch(result) { + case BT_ERROR_NONE: + case BT_ERROR_ALREADY_DONE: + case BT_ERROR_NOT_ENABLED: + util::ReportSuccess(response->get()); + break; + case BT_ERROR_NOW_IN_PROGRESS: + throw ServiceNotAvailableException("Bluetooth device is busy"); + default: + throw UnknownException("Unknown exception"); + } + } catch (const PlatformException& err) { + util::ReportError(err, response->get()); + } + + util::AsyncResponse( + adapter->user_request_callback_[SET_POWERED], response); + adapter->user_request_list_[SET_POWERED] = false; + } +} + +void BluetoothAdapter::NameChangedCB(char *name, void *user_data) { + LoggerD("Entered"); + + BluetoothAdapter* adapter = static_cast(user_data); + if (!adapter) { + LoggerD("User data is NULL"); + return; + } + + picojson::value value = picojson::value(picojson::object()); + picojson::object* data_obj = &value.get(); + + data_obj->insert(std::make_pair(kAction, picojson::value(kOnNameChanged))); + data_obj->insert(std::make_pair(kName, picojson::value(name))); + + util::FireEvent(kAdapterChangeCallbackEvent, value); + + if (adapter->user_request_list_[SET_NAME] && name == adapter->requested_name_) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::AsyncResponse(adapter->user_request_callback_[SET_NAME], response); + adapter->user_request_list_[SET_NAME] = false; + } +} + +void BluetoothAdapter::VisibilityChangedCB(int result, bt_adapter_visibility_mode_e mode, void *user_data) { + LoggerD("Entered"); + + BluetoothAdapter* adapter = static_cast(user_data); + if (!adapter) { + LoggerD("User data is NULL"); + return; + } + + bool visible = BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE != mode; + bool previous_visible = adapter->is_visible_; + adapter->is_visible_ = visible; + + if (previous_visible != visible) { + picojson::value value = picojson::value(picojson::object()); + picojson::object* data_obj = &value.get(); + + data_obj->insert(std::make_pair(kAction, picojson::value(kOnVisibilityChanged))); + data_obj->insert(std::make_pair(kAdapterVisible, picojson::value(visible))); + + util::FireEvent(kAdapterChangeCallbackEvent, value); + } + + if (adapter->user_request_list_[SET_VISIBLE] && adapter->requested_visibility_ == mode) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + + if (BT_ERROR_NONE == result) { + util::ReportSuccess(response->get()); + } else { + util::ReportError(UnknownException("Unknown exception"), + response->get()); + } + + util::AsyncResponse( + adapter->user_request_callback_[SET_VISIBLE], response); + adapter->user_request_list_[SET_VISIBLE] = false; + } +} + +static bool ForeachBondedDevicesCB(bt_device_info_s *device_info, void *user_data) +{ + LoggerD("Entered"); + if (nullptr == user_data) { + LoggerD("user data is NULL."); + return false; + } + + if (nullptr == device_info) { + LoggerD("Device info is not valid."); + return false; + } + + picojson::array* array = static_cast(user_data); + for (auto iter = array->begin(); iter != array->end(); iter++) { + if (!strcmp(device_info->remote_address, ((*iter).get()) + .find(kDeviceAddress)->second.get().c_str())) { + BluetoothDevice::ToJson(device_info, &iter->get()); + return true; + } + } + + array->push_back(picojson::value(picojson::object())); + + BluetoothDevice::ToJson(device_info, &array->back().get()); + return true; +} + +void BluetoothAdapter::DiscoveryStateChangedCB( + int result, + bt_adapter_device_discovery_state_e discovery_state, + bt_adapter_device_discovery_info_s *discovery_info, + void *user_data) +{ + LoggerD("Entered"); + + BluetoothAdapter* adapter = static_cast(user_data); + if (!adapter) { + LoggerD("User data is NULL"); + return; + } + + picojson::value value = picojson::value(picojson::object()); + picojson::object* data_obj = &value.get(); + + switch(discovery_state) { + case BT_ADAPTER_DEVICE_DISCOVERY_STARTED: { + if (adapter->user_request_list_[DISCOVER_DEVICES]) { + if (BT_ERROR_NONE == result) { + //store addresses of previously found devices into disappeared_addresses + adapter->disappeared_addresses_ = adapter->discovered_addresses_; + adapter->discovered_addresses_.clear(); + adapter->discovered_devices_.clear(); + + data_obj->insert(std::make_pair(kAction, picojson::value(kOnDiscoverStarted))); + util::FireEvent(kAdapterDiscoverSuccessEvent, value); + } else { + util::ReportError(UnknownException("Unknown error"), *data_obj); + util::FireEvent(kAdapterDiscoverErrorEvent, value); + adapter->user_request_list_[DISCOVER_DEVICES] = false; + } + } + break; + } + case BT_ADAPTER_DEVICE_DISCOVERY_FINISHED: { + if (BT_ERROR_NONE == result || BT_ERROR_CANCELLED == result) { + if (adapter->user_request_list_[DISCOVER_DEVICES]) { + data_obj->insert(std::make_pair(kAction, picojson::value(kOnDiscoverFinished))); + + for (auto it : adapter->disappeared_addresses_) { + picojson::value disapeared_val = picojson::value(picojson::object()); + picojson::object* disapeared_obj = &disapeared_val.get(); + + disapeared_obj->insert(std::make_pair(kAction, + picojson::value(kOnDiscoverDisappeared))); + disapeared_obj->insert(std::make_pair(kData, picojson::value(it))); + + util::FireEvent(kAdapterDiscoverSuccessEvent, disapeared_val); + } + + data_obj->insert(std::make_pair(kData, + picojson::value(adapter->discovered_devices_))); + util::FireEvent(kAdapterDiscoverSuccessEvent, value); + + adapter->user_request_list_[DISCOVER_DEVICES] = false; + } + + if (adapter->user_request_list_[STOP_DISCOVERY]) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + + util::ReportSuccess(response->get()); + util::AsyncResponse( + adapter->user_request_callback_[STOP_DISCOVERY], response); + + adapter->user_request_list_[STOP_DISCOVERY] = false; + } + } + break; + } + case BT_ADAPTER_DEVICE_DISCOVERY_FOUND: { + if (adapter->user_request_list_[DISCOVER_DEVICES]) { + if (BT_ERROR_NONE == result && + adapter->discovered_addresses_.insert( + discovery_info->remote_address).second) { + adapter->disappeared_addresses_.erase(discovery_info->remote_address); + + data_obj->insert(std::make_pair(kAction, picojson::value(kOnDiscoverFound))); + picojson::value& data = data_obj->insert(std::make_pair(kData, + picojson::value(picojson::object()))).first->second; + + BluetoothDevice::ToJson(discovery_info, &data.get()); + adapter->discovered_devices_.push_back(data); + + util::FireEvent(kAdapterDiscoverSuccessEvent, value); + } + } + break; + } + default: + LoggerD("Unknown state"); + break; + } +} + +BluetoothAdapter::BluetoothAdapter() : + is_visible_(false), + is_powered_(false), + is_initialized_(false) +{ + LoggerD("Entered"); + if (BT_ERROR_NONE == bt_initialize()) { + LoggerD("Bluetooth service is initialized."); + is_initialized_ = true; + + int ret = BT_ERROR_NONE; + ret |= bt_adapter_set_device_discovery_state_changed_cb(DiscoveryStateChangedCB, this); + ret |= bt_adapter_set_state_changed_cb(StateChangedCB, this); + ret |= bt_adapter_set_name_changed_cb(NameChangedCB, this); + ret |= bt_adapter_set_visibility_mode_changed_cb(VisibilityChangedCB, this); + + if (BT_ERROR_NONE != ret) { + LoggerE("Setting listeners function failed."); + } + } else { + LoggerE("Bluetooth service initialization failed."); + } + + bt_adapter_state_e state; + if (BT_ERROR_NONE == bt_adapter_get_state(&state)) { + is_powered_ = BT_ADAPTER_ENABLED == state; + } + + bt_adapter_visibility_mode_e mode; + if (BT_ERROR_NONE == bt_adapter_get_visibility(&mode, nullptr)) { + is_visible_ = BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE != mode; + } +} + +BluetoothAdapter::~BluetoothAdapter() { + bt_socket_unset_data_received_cb(); + bt_socket_unset_connection_state_changed_cb(); + + for (auto it : connected_sockets_) { + bt_socket_disconnect_rfcomm(it); + } + + for (auto it : registered_uuids_) { + bt_socket_destroy_rfcomm(it.second.first); + } + + bt_adapter_unset_state_changed_cb(); + bt_adapter_unset_name_changed_cb(); + bt_adapter_unset_visibility_mode_changed_cb(); + bt_adapter_unset_device_discovery_state_changed_cb(); + + if (is_initialized_) { + if (BT_ERROR_NONE == bt_deinitialize()) { + LoggerD("Bluetooth service is deinitialized."); + } else { + LoggerE("Bluetooth service deinitialization failed."); + } + } +} + +BluetoothAdapter& BluetoothAdapter::GetInstance() { + static BluetoothAdapter instance; + return instance; +} + +std::string BluetoothAdapter::get_name() const { + char* name = nullptr; + std::string str_name = ""; + if (BT_ERROR_NONE == bt_adapter_get_name(&name)) { + if (name) { + str_name = name; + free(name); + } + } + + return str_name; +} + +bool BluetoothAdapter::get_visible() const { + bt_adapter_visibility_mode_e mode; + + if (BT_ERROR_NONE == bt_adapter_get_visibility(&mode, NULL)) { + return mode != BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE; + } + + return false; +} + +void BluetoothAdapter::set_visible(bool visible) { + is_visible_ = visible; +} + +bool BluetoothAdapter::get_powered() { + return is_powered_; +} + +void BluetoothAdapter::set_powered(bool powered) { + is_powered_ = powered; +} + +bool BluetoothAdapter::is_initialized() const { + return is_initialized_; +} + +void BluetoothAdapter::SetName(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothAdmin); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + const auto& args = util::GetArguments(data); + const auto name = FromJson(args, "name"); + + try { + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + + if (this->get_powered()) { + if (get_name() == name) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportSuccess(response->get()); + util::AsyncResponse(callback_handle, response); + return; + } + + if (this->user_request_list_[SET_NAME]) { + throw ServiceNotAvailableException("Already requested"); + } + + this->user_request_list_[SET_NAME] = true; + this->user_request_callback_[SET_NAME] = callback_handle; + + int ret = bt_adapter_set_name(name.c_str()); + switch(ret) { + case BT_ERROR_NONE: + //bt_adapter_name_changed_cb() will be invoked + //if this function returns #BT_ERROR_NONE + this->requested_name_ = name; + break; + case BT_ERROR_INVALID_PARAMETER: + throw InvalidValuesException("Invalid value"); + default: + throw UnknownException("Unknown exception"); + } + } else { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + util::AsyncResponse(callback_handle, response); + this->user_request_list_[SET_NAME] = false; + return; + } + + util::ReportSuccess(out); +} + +void BluetoothAdapter::SetPowered(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothAdmin); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + const auto& args = util::GetArguments(data); + const auto new_powered = FromJson(args, "powered"); + + try { + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + + if (this->user_request_list_[SET_POWERED]) { + throw ServiceNotAvailableException("Already requested"); + } + + bool cur_powered = this->get_powered(); + + if (new_powered != cur_powered) { + this->requested_powered_ = new_powered; + this->user_request_list_[SET_POWERED] = true; + this->user_request_callback_[SET_POWERED] = callback_handle; + + if (new_powered) { + bt_adapter_enable(); + } else { + bt_adapter_disable(); + } + } else { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportSuccess(response->get()); + util::AsyncResponse(callback_handle, response); + return; + } + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + util::AsyncResponse(callback_handle, response); + return; + } + + util::ReportSuccess(out); +} + +void BluetoothAdapter::SetVisible(const picojson::value& data, picojson::object& out) +{ + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothManager); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + const auto& args = util::GetArguments(data); + const auto visible = FromJson(args, "visible"); + + unsigned short timeout = kTimeout; + if (visible) { + timeout = static_cast(FromJson(args, "timeout")); + } + + try { + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + + if (this->user_request_list_[SET_VISIBLE]) { + throw ServiceNotAvailableException("Already requested"); + } + + if (this->get_powered()) { + bt_adapter_visibility_mode_e mode = BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE; + if (visible) { + if (0 == timeout) { + mode = BT_ADAPTER_VISIBILITY_MODE_GENERAL_DISCOVERABLE; + } else { + mode = BT_ADAPTER_VISIBILITY_MODE_LIMITED_DISCOVERABLE; + } + } + + bt_adapter_visibility_mode_e current = BT_ADAPTER_VISIBILITY_MODE_NON_DISCOVERABLE; + int time = 0; + if (BT_ERROR_NONE != bt_adapter_get_visibility(¤t , &time)) { + throw UnknownException("Unknown exception"); + } + + if (mode == current) { + if (BT_ADAPTER_VISIBILITY_MODE_LIMITED_DISCOVERABLE != mode || + (unsigned int)time != timeout) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportSuccess(response->get()); + util::AsyncResponse(callback_handle, response); + return; + } + } + + this->requested_visibility_ = mode; + this->user_request_list_[SET_VISIBLE] = true; + this->user_request_callback_[SET_VISIBLE] = callback_handle; + int ret = bt_adapter_set_visibility(mode, timeout); + + switch(ret) { + case BT_ERROR_NONE: + //bt_adapter_visibility_mode_changed_cb() will be invoked + //if this function returns #BT_ERROR_NONE + break; + case BT_ERROR_INVALID_PARAMETER: + throw InvalidValuesException("Invalid value"); + default: + throw UnknownException("Unknown exception"); + } + } else { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + util::AsyncResponse(callback_handle, response); + return; + } + + util::ReportSuccess(out); +} + +void BluetoothAdapter::DiscoverDevices(const picojson::value& /* data */, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothGap); + + try { + if (!is_initialized_) { + throw UnknownException("Bluetooth service is not initialized."); + } + + if (this->user_request_list_[DISCOVER_DEVICES]) { + throw ServiceNotAvailableException("Already requested"); + } + + if (!get_powered()) { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + + this->user_request_list_[DISCOVER_DEVICES] = true; + bt_adapter_start_device_discovery(); + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + TaskQueue::GetInstance().Async([](const std::shared_ptr& result) { + util::FireEvent(kAdapterDiscoverErrorEvent, result); + }, response); + return; + } + + util::ReportSuccess(out); +} + +void BluetoothAdapter::StopDiscovery(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothGap); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + + try { + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + + if (this->user_request_list_[STOP_DISCOVERY]) { + throw ServiceNotAvailableException("Already requested"); + } + + if (this->get_powered()) { + bool is_discovering = false; + bt_adapter_is_discovering(&is_discovering); + + if (!is_discovering) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportSuccess(response->get()); + util::AsyncResponse(callback_handle, response); + return; + } + + this->user_request_list_[STOP_DISCOVERY] = true; + this->user_request_callback_[STOP_DISCOVERY] = callback_handle; + int ret = bt_adapter_stop_device_discovery(); + switch(ret) { + case BT_ERROR_NONE: { + //This function invokes bt_adapter_device_discovery_state_changed_cb(). + break; + } + default: { + this->user_request_list_[STOP_DISCOVERY] = false; + throw UnknownException("Unknown error"); + } + } + } else { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + util::AsyncResponse(callback_handle, response); + return; + } + + util::ReportSuccess(out); +} + +void BluetoothAdapter::GetKnownDevices(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothGap); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + + auto get_known_devices = [this](const std::shared_ptr& response) -> void { + try { + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + if (this->get_powered()) { + picojson::object& response_obj = response->get(); + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + picojson::array& array = result_obj.insert( + std::make_pair("devices", picojson::value( + picojson::array()))).first->second.get(); + + array = discovered_devices_; + + if (BT_ERROR_NONE == bt_adapter_foreach_bonded_device( + ForeachBondedDevicesCB, &array)) { + util::ReportSuccess(result, response_obj); + } else { + throw UnknownException("Unknown exception"); + } + } else { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& err) { + util::ReportError(err, response->get()); + } + }; + auto get_known_devices_response = [callback_handle]( + const std::shared_ptr& response) -> void { + util::SyncResponse(callback_handle, response); + }; + + TaskQueue::GetInstance().Queue( + get_known_devices, + get_known_devices_response, + std::shared_ptr(new picojson::value(picojson::object()))); + + util::ReportSuccess(out); +} + +void BluetoothAdapter::GetDevice(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothGap); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + const auto& args = util::GetArguments(data); + + const auto& address = FromJson(args, "address"); + + auto get_device = [this, address](const std::shared_ptr& response) -> void { + try { + if (!IsValidAddress(address)) { + throw NotFoundException("Wrong address"); + } + + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + if (this->get_powered()) { + picojson::object& response_obj = response->get(); + bt_device_info_s *info = nullptr; + + if (bt_adapter_get_bonded_device_info(address.c_str(), &info) == BT_ERROR_NONE && + info != nullptr) { + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + + BluetoothDevice::ToJson(info, &result_obj); + util::ReportSuccess(result, response_obj); + bt_adapter_free_device_info(info); + return; + } + + auto is_address = discovered_addresses_.find(address); + if (is_address != discovered_addresses_.end()) { + for (auto iter = discovered_devices_.begin(); + iter != discovered_devices_.end(); iter++) { + if (!strcmp(address.c_str(), ((*iter).get()) + .find(kDeviceAddress)->second.get().c_str())) { + util::ReportSuccess(*iter, response_obj); + return; + } + } + } else { + throw NotFoundException("There is no device with the given address"); + } + } else { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& err) { + util::ReportError(err, response->get()); + } + }; + + auto get_device_response = [callback_handle]( + const std::shared_ptr& response) -> void { + util::SyncResponse(callback_handle, response); + }; + + TaskQueue::GetInstance().Queue( + get_device, + get_device_response, + std::shared_ptr(new picojson::value(picojson::object()))); + + util::ReportSuccess(out); +} + +class BondingHandler { +public: + BondingHandler(double callback_handle, const std::string& address) : + callback_handle_(callback_handle), address_(address) {} + + void set_address(const std::string& address) { + address_ = address; + } + + const std::string& address() const { + return address_; + } + + void Invoke(const std::shared_ptr& response) { + LoggerD("Entered"); + util::AsyncResponse(callback_handle_, response); + } + +private: + double callback_handle_; + std::string address_; +}; + +void BluetoothAdapter::CreateBonding(const picojson::value& data, picojson::object& out) +{ + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothGap); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + const auto& args = util::GetArguments(data); + + const auto& address = FromJson(args, "address"); + + auto create_bonding = [address, callback_handle, this]() -> void { + try { + if(!IsValidAddress(address)) { + throw InvalidValuesException("Wrong address"); + } + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + + if (this->get_powered()) { + + auto bond_create_callback = [](int callback_result, + bt_device_info_s *device_info, + void *user_data) { + LoggerD("bond_create_callback"); + + BondingHandler* handler = static_cast(user_data); + if (!handler) { + LoggerW("user_data is nullptr"); + return; + } + if (!device_info) { + LoggerW("device_info is nullptr"); + return; + } + + if (!strcmp(handler->address().c_str(), device_info->remote_address)) { // requested event + try { + if (BT_ERROR_NONE == callback_result && nullptr != device_info ) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + picojson::object& response_obj = response->get(); + picojson::value result = picojson::value(picojson::object()); + picojson::object& result_obj = result.get(); + + BluetoothDevice::ToJson(device_info, &result_obj); + util::ReportSuccess(result, response_obj); + handler->Invoke(response); + } else if (BT_ERROR_REMOTE_DEVICE_NOT_FOUND == callback_result) { + LoggerE("Not found"); + throw ServiceNotAvailableException("Not found"); + } else { + LoggerE("Unknown exception"); + throw UnknownException("Unknown exception"); + } + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + handler->Invoke(response); + } + delete handler; + bt_device_unset_bond_created_cb(); + } else { // unexpected event + LoggerD("An unexpected bonding detected"); + } + }; + + BondingHandler* handler = new BondingHandler(callback_handle, address); + bt_device_set_bond_created_cb(bond_create_callback, handler); + int ret = bt_device_create_bond(address.c_str()); + + switch(ret) { + case BT_ERROR_NONE: + { + LoggerD("bt_device_create_bond() succeeded"); + break; + } + case BT_ERROR_INVALID_PARAMETER: + { + LoggerE("Not found"); + bt_device_unset_bond_created_cb(); + delete handler; + throw InvalidValuesException("Invalid value"); + } + default: + { + LoggerE("Unknown exception"); + bt_device_unset_bond_created_cb(); + delete handler; + throw UnknownException("Unknown exception"); + } + } + } else { // Not powered + LoggerE("Bluetooth device is turned off"); + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + util::AsyncResponse(callback_handle, response); + } + }; + TaskQueue::GetInstance().Queue(create_bonding); + util::ReportSuccess(out); +} + +void BluetoothAdapter::DestroyBonding(const picojson::value& data, picojson::object& out) +{ + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothGap); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + const auto& args = util::GetArguments(data); + + const auto& address = FromJson(args, "address"); + + auto destroy_bonding = [address, callback_handle, this]() -> void { + try { + if(!IsValidAddress(address)) { + throw InvalidValuesException("Wrong address"); + } + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + + if (this->get_powered()) { + bt_device_info_s *device_info = nullptr; + int ret = bt_adapter_get_bonded_device_info(address.c_str(), &device_info); + + if (BT_ERROR_NONE != ret || nullptr == device_info) { + LoggerD("There is no bonding"); + throw NotFoundException("Not found"); + } else { + bt_adapter_free_device_info(device_info); + + auto bond_destroy_callback = [](int callback_result, + char *remote_address, + void *user_data) { + LoggerD("bond_destroy_callback"); + + BondingHandler* handler = static_cast(user_data); + if (!handler) { + LoggerW("user_data is nullptr"); + return; + } + + if (!strcmp(handler->address().c_str(), remote_address)) { // requested event + try { + if (BT_ERROR_NONE == callback_result) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportSuccess(response->get()); + handler->Invoke(response); + } else { + LoggerE("Unknown exception"); + throw UnknownException("Unknown exception"); + } + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + handler->Invoke(response); + } + delete handler; + bt_device_unset_bond_destroyed_cb(); + } else { // unexpected event + LoggerD("An unexpected bonding detected"); + } + }; + + BondingHandler* handler = new BondingHandler(callback_handle, address); + bt_device_set_bond_destroyed_cb(bond_destroy_callback, handler); + + int ret = bt_device_destroy_bond(address.c_str()); + + switch(ret) { + case BT_ERROR_NONE: + { + LoggerD("bt_device_destroy_bond() succeeded"); + break; + } + case BT_ERROR_INVALID_PARAMETER: + { + LoggerE("Not found"); + bt_device_unset_bond_destroyed_cb(); + delete handler; + throw InvalidValuesException("Invalid value"); + } + default: + { + LoggerE("Unknown exception"); + bt_device_unset_bond_destroyed_cb(); + delete handler; + throw UnknownException("Unknown exception"); + } + } + } + } else { // Not powered + LoggerE("Bluetooth device is turned off"); + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& err) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(err, response->get()); + util::AsyncResponse(callback_handle, response); + } + }; + TaskQueue::GetInstance().Queue(destroy_bonding); + util::ReportSuccess(out); +} + +void BluetoothAdapter::RegisterRFCOMMServiceByUUID(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothSpp); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + const auto& args = util::GetArguments(data); + + const auto& uuid = FromJson(args, "uuid"); + const auto& name = FromJson(args, "name"); + + auto rfcomm = [this, uuid, name](const std::shared_ptr& response) -> void { + try { + if (!this->is_initialized()) { + throw UnknownException("Bluetooth service is not initialized."); + } + + if (!IsValidUUID(uuid)) { + throw InvalidValuesException("Wrong UUID"); + } + + if (this->get_powered()) { + bool is_registered = false; + int ret = bt_adapter_is_service_used(uuid.c_str(), &is_registered); + + if (BT_ERROR_NONE == ret && is_registered) { + throw InvalidValuesException("Already registered"); + } + + int socket = -1; + ret = bt_socket_create_rfcomm(uuid.c_str(), &socket); + + switch (ret) { + case BT_ERROR_NONE: { + int ret_in = bt_socket_listen_and_accept_rfcomm(socket, 0); + switch(ret_in) { + case BT_ERROR_NONE: { + LoggerD("bt_socket_listen() succeeded"); + bt_socket_set_connection_state_changed_cb(OnSocketConnected, this); + + registered_uuids_.insert(std::make_pair(uuid, + std::make_pair(socket, false))); + + util::ReportSuccess(response->get()); + break; + } + + case BT_ERROR_INVALID_PARAMETER: { + throw InvalidValuesException("Invalid value"); + break; + } + + default: { + throw UnknownException("Unknown error"); + break; + } + } + break; + } + + case BT_ERROR_INVALID_PARAMETER: + throw InvalidValuesException("Invalid value"); + break; + + default: + throw UnknownException("Unknown error"); + break; + } + } else { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& err) { + util::ReportError(err, response->get()); + } + }; + + auto rfcomm_response = [callback_handle](const std::shared_ptr& response) -> void { + util::SyncResponse(callback_handle, response); + }; + + TaskQueue::GetInstance().Queue(rfcomm, rfcomm_response, + std::shared_ptr(new picojson::value(picojson::object()))); + + util::ReportSuccess(out); +} + +void BluetoothAdapter::UnregisterUUID(const std::string& uuid, int callback_handle) { + LoggerD("Entered"); + + if (!IsValidUUID(uuid)) { + throw InvalidValuesException("Wrong UUID"); + } + + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + + try { + if (is_powered_) { + auto iter = registered_uuids_.find(uuid); + if (iter != registered_uuids_.end()) { + if (BT_ERROR_NONE == bt_socket_destroy_rfcomm(iter->second.first)) { + registered_uuids_.erase(iter); + } else { + throw UnknownException("Unknown error"); + } + } + + if (registered_uuids_.size() == 0 && + connection_requests_.size() == 0 && + connected_sockets_.size() == 0) { + bt_socket_unset_connection_state_changed_cb(); + } + } else { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + + util::ReportSuccess(response->get()); + } catch (const PlatformException& err) { + util::ReportError(err, response->get()); + } + + util::AsyncResponse(callback_handle, response); +} + +void BluetoothAdapter::GetBluetoothProfileHandler(const picojson::value& data, + picojson::object& out) { + LoggerD("Entered"); + + const auto& args = util::GetArguments(data); + auto profile = FromJson(args, "profileType"); + + if (kBluetoothProfileHealth == profile) { + bool supported = false; + if (SYSTEM_INFO_ERROR_NONE != system_info_get_platform_bool(kFeatureBluetoothHealth.c_str(), + &supported)) { + LoggerW("Can't check if BT health profile is supported or not"); + } + + if (!supported) { + throw NotSupportedException("Bluetooth health profile is not supported"); + } else { + LoggerD("BT health profile is supported"); + } + } else { + throw TypeMismatchException("Wrong profile type."); + } + + util::ReportSuccess(out); +} + +void BluetoothAdapter::GetName(const picojson::value& /* data */, picojson::object& out) { + LoggerD("Entered"); + + util::ReportSuccess(picojson::value(get_name()), out); +} + +void BluetoothAdapter::GetAddress(const picojson::value& /* data */, picojson::object& out) { + LoggerD("Entered"); + + if (!is_initialized_) { + throw UnknownException("Bluetooth service is not initialized."); + } + + std::string str_address = ""; + char* address = nullptr; + if (BT_ERROR_NONE == bt_adapter_get_address(&address)) { + if (address) { + str_address = address; + free(address); + } + } + + util::ReportSuccess(picojson::value(str_address), out); +} + +void BluetoothAdapter::GetPowered(const picojson::value& /* data */, picojson::object& out) { + LoggerD("Entered"); + + util::ReportSuccess(picojson::value(is_powered_), out); +} + +void BluetoothAdapter::GetVisible(const picojson::value& /* data */, picojson::object& out) { + LoggerD("Entered"); + + util::ReportSuccess(picojson::value(get_visible()), out); +} + +void BluetoothAdapter::OnSocketConnected(int result, + bt_socket_connection_state_e state, + bt_socket_connection_s* connection, + void* user_data) { + LoggerD("Entered"); + + BluetoothAdapter* object = static_cast(user_data); + + if (!object) { + LoggerW("user_data is NULL"); + return; + } + + if (!connection) { + LoggerW("connection is NULL"); + return; + } + + if (BT_SOCKET_SERVER == connection->local_role) { + LoggerD("Server"); + + const auto iter = object->registered_uuids_.find(connection->service_uuid); + if (iter == object->registered_uuids_.end()) { + LoggerW("Connection state has changed unexpectedly"); + return; + } + + if (BT_SOCKET_CONNECTED == state) { // connected when Server + if (BT_ERROR_NONE == result) { + // Update registered_uuids_ state + iter->second.second = true; + + // Call BluetoothServiceHandler.onconnect + util::FireEvent("BLUETOOTH_SERVICE_ONCONNECT", BluetoothSocket::ToJson(connection)); + + // Update connected_sockets_ + object->connected_sockets_.push_back(connection->socket_fd); + bt_socket_set_data_received_cb(OnSocketReceivedData, user_data); + } else { + LoggerW("Establishing a connection failed"); + } + return; + } else { // disconnected when Server + if (BT_ERROR_NONE == result) { + // Update registered_uuids_ state + iter->second.second = false; + + object->RemoveSocket(connection->socket_fd); + } + else { + LoggerW("Disconnecting a connection failed"); + } + } + } else if (BT_SOCKET_CLIENT == connection->local_role) { + LoggerD("Client"); + + if (BT_SOCKET_CONNECTED == state) { // connected when Client + const auto range = object->connection_requests_.equal_range(connection->remote_address); + const auto end = object->connection_requests_.end(); + auto request = end; + + for (auto it = range.first; it != range.second; ++it) { + if (strcmp(it->second->uuid_.c_str(), connection->service_uuid) == 0) { + request = it; + break; + } + } + + if (end == request) { + LoggerW("Connection state has changed unexpectedly"); + return; + } + + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + + if (BT_ERROR_NONE == result) { + object->connected_sockets_.push_back(connection->socket_fd); + bt_socket_set_data_received_cb(OnSocketReceivedData, user_data); + + util::ReportSuccess(BluetoothSocket::ToJson(connection), + response->get()); + } else { + util::ReportError(NotFoundException("Not found"), + response->get()); + } + + util::AsyncResponse(request->second->callback_handle_, response); + + // request has been handled, can be safely removed + object->connection_requests_.erase(request); + } else { // disconnected when Client + if (result == BT_ERROR_NONE) { + object->RemoveSocket(connection->socket_fd); + } else { + LoggerW("Disconnecting a connection failed"); + } + } + } else { + LoggerW("Unknown role"); + return; + } + + if (object->connected_sockets_.size() == 0) { + bt_socket_unset_data_received_cb(); + } + + if (object->registered_uuids_.size() == 0 && + object->connection_requests_.size() == 0 && + object->connected_sockets_.size() == 0) { + bt_socket_unset_connection_state_changed_cb(); + } +} + +void BluetoothAdapter::OnSocketReceivedData(bt_socket_received_data_s* data, void* user_data) { + LoggerD("Entered"); + + BluetoothAdapter* object = static_cast(user_data); + + if (!object) { + LoggerW("user_data is NULL"); + return; + } + + if (!data) { + LoggerW("data is NULL"); + return; + } + + const auto it = std::find(object->connected_sockets_.begin(), + object->connected_sockets_.end(), + data->socket_fd); + + if (it == object->connected_sockets_.end()) { + LoggerW("Unknown connected socket: %d", data->socket_fd); + return; + } + + // Store received data + object->StoreSocketData(data); + + InvokeSocketOnMessageEvent(*it); +} + +void BluetoothAdapter::ConnectToServiceByUUID(const std::string& address, + const std::string& uuid, + double callback_handle) { + LoggerD("Entered"); + + if (!IsValidUUID(uuid)) { + throw InvalidValuesException("Wrong UUID"); + } + + try { + if (is_powered_) { + int ret = bt_socket_connect_rfcomm(address.c_str(), uuid.c_str()); + + switch (ret) { + case BT_ERROR_NONE: { + LoggerD("bt_socket_connect_rfcomm() succeeded"); + + ConnectionRequestPtr request{new ConnectionRequest()}; + request->uuid_ = uuid; + request->callback_handle_ = callback_handle; + connection_requests_.insert(std::make_pair(address, request)); + + bt_socket_set_connection_state_changed_cb(OnSocketConnected, this); + break; + } + + case BT_ERROR_INVALID_PARAMETER: + case BT_ERROR_REMOTE_DEVICE_NOT_BONDED: + throw InvalidValuesException("Invalid value"); + break; + + default: + throw UnknownException("Unknown error"); + break; + } + } else { + throw ServiceNotAvailableException("Bluetooth device is turned off"); + } + } catch (const PlatformException& e) { + std::shared_ptr response = + std::shared_ptr(new picojson::value(picojson::object())); + util::ReportError(e, response->get()); + util::AsyncResponse(callback_handle, response); + } +} + + +static void InvokeSocketEvent(int id, const char* event) { + picojson::value value = picojson::value(picojson::object()); + picojson::object& value_obj = value.get(); + value_obj.insert(std::make_pair("id", picojson::value(std::to_string(id)))); + value_obj.insert(std::make_pair("event", picojson::value(event))); + util::FireEvent("BLUETOOTH_SOCKET_STATE_CHANGED", value); +} + +void BluetoothAdapter::InvokeSocketOnMessageEvent(int id) { + InvokeSocketEvent(id, "onmessage"); +} + +void BluetoothAdapter::InvokeSocketOnCloseEvent(int id) { + InvokeSocketEvent(id, "onclose"); +} + +void BluetoothAdapter::RemoveSocket(int socket) { + const auto data_it = socket_data_.find(socket); + + if (data_it != socket_data_.end()) { + socket_data_.erase(data_it); + } else { + LoggerD("No stored data for socket: %d", socket); + } + + const auto it = std::find(connected_sockets_.begin(), + connected_sockets_.end(), + socket); + + if (it == connected_sockets_.end()) { + LoggerW("Unknown connected socket: %d", socket); + return; + } + + connected_sockets_.erase(it); + + BluetoothAdapter::InvokeSocketOnCloseEvent(*it); +} + +void BluetoothAdapter::StoreSocketData(bt_socket_received_data_s* data) { + LoggerD("Entered"); + + auto data_store = socket_data_[data->socket_fd]; + + for (int i = 0; i < data->data_size; ++i) { + data_store.push_back(data->data[i]); + } +} + +const std::list& BluetoothAdapter::ReadSocketData(int socket) { + LoggerD("Entered"); + + return socket_data_[socket]; +} + +void BluetoothAdapter::ClearSocketData(int socket) { + LoggerD("Entered"); + + const auto data_it = socket_data_.find(socket); + + if (data_it != socket_data_.end()) { + data_it->second.clear(); + } +} + +void BluetoothAdapter::IsServiceConnected(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + const auto& args = util::GetArguments(data); + const auto& uuid = FromJson(args, "uuid"); + + auto iter = registered_uuids_.find(uuid); + if (iter == registered_uuids_.end()) { + throw InvalidValuesException("Invalid parameter was passed."); + } + + util::ReportSuccess(picojson::value(iter->second.second), out); +} + +} // namespace bluetooth +} // namespace extension diff --git a/src/bluetooth/bluetooth_adapter.h b/src/bluetooth/bluetooth_adapter.h new file mode 100644 index 00000000..827668f4 --- /dev/null +++ b/src/bluetooth/bluetooth_adapter.h @@ -0,0 +1,380 @@ +/* + * 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_ADAPTER_H_ +#define BLUETOOTH_BLUETOOTH_ADAPTER_H_ + +#include +#include +#include +#include +#include + +#include + +#include "common/picojson.h" + +namespace extension { +namespace bluetooth { + +enum AdapterAsyncEvent { + SET_POWERED = 0, + SET_NAME, + SET_VISIBLE, + DISCOVER_DEVICES, + STOP_DISCOVERY +}; + +class BluetoothAdapter { +public: + /** + * Signature: @code void setName(name, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_setName', args: {name: name}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void SetName(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void setPowered(state, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_setPowered', args: {state: state}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void SetPowered(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void setVisible(mode, successCallback, errorCallback, timeout); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_setVisible', args: {mode: mode, timeout: timeout}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void SetVisible(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void discoverDevices(discoveryCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_discoverDevices', args: {discoveryCallbackId: id}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Discovery callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {event, eventData}} + * @endcode + */ + void DiscoverDevices(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void stopDiscovery(successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_stopDiscovery', args: {}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void StopDiscovery(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void getKnownDevices(successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_getKnownDevices', args: {}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {devices[]}} + * @endcode + */ + void GetKnownDevices(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void getDevice(address, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_getDevice', args: {address: address}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {device}} + * @endcode + */ + void GetDevice(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void createBonding(address, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_createBonding', args: {address: address}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {device}} + * @endcode + */ + void CreateBonding(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void destroyBonding(address, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_destroyBonding', args: {address: address}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void DestroyBonding(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void registerRFCOMMServiceByUUID(uuid, name, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_registerRFCOMMServiceByUUID', + * args: {uuid: uuid, name: name}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {handler}} + * @endcode + */ + void RegisterRFCOMMServiceByUUID(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code BluetoothProfileHandler getBluetoothProfileHandler(profileType); @endcode + * JSON: @code data: {method: 'BluetoothAdapter_getBluetoothProfileHandler', + * args: {profileType: profileType}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {BluetoothProfileHandler}} + * @endcode + */ + void GetBluetoothProfileHandler(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code BluetoothAdapter.name; @endcode + * JSON: @code data: {method: 'BluetoothAdapter_getName', args: {}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: name} + * @endcode + */ + void GetName(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code BluetoothAdapter.address; @endcode + * JSON: @code data: {method: 'BluetoothAdapter_getAddress', args: {}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: address} + * @endcode + */ + void GetAddress(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code BluetoothAdapter.powered; @endcode + * JSON: @code data: {method: 'BluetoothAdapter_getPowered', args: {}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: powered} + * @endcode + */ + void GetPowered(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code BluetoothAdapter.visible; @endcode + * JSON: @code data: {method: 'BluetoothAdapter_getVisible', args: {}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: visible} + * @endcode + */ + void GetVisible(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code BluetoothServiceHandler.isConnected; @endcode + * JSON: @code data: {method: 'BluetoothAdapter_isConnected', args: {}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: isConnected} + * @endcode + */ + void IsServiceConnected(const picojson::value& data, picojson::object& out); + + static BluetoothAdapter& GetInstance(); + virtual ~BluetoothAdapter(); + + std::string get_name() const; + bool get_visible() const; + void set_visible(bool visible); + bool get_powered(); + void set_powered(bool powered); + bool is_initialized() const; + + void ConnectToServiceByUUID(const std::string& address, + const std::string& uuid, + double callback_handle); + + const std::list& ReadSocketData(int socket); + + void ClearSocketData(int socket); + + void UnregisterUUID(const std::string& uuid, int callback_handle); + +private: + BluetoothAdapter(); + BluetoothAdapter(const BluetoothAdapter&) = delete; + BluetoothAdapter& operator=(const BluetoothAdapter&) = delete; + + static void StateChangedCB(int result, + bt_adapter_state_e state, + void *user_data); + static void NameChangedCB(char *name, + void *user_data); + static void VisibilityChangedCB(int result, + bt_adapter_visibility_mode_e mode, + void *user_data); + static void DiscoveryStateChangedCB(int result, + bt_adapter_device_discovery_state_e discovery_state, + bt_adapter_device_discovery_info_s *discovery_info, + void *user_data); + + void StoreSocketData(bt_socket_received_data_s* data); + + void RemoveSocket(int socket); + + static void OnSocketConnected(int result, + bt_socket_connection_state_e state, + bt_socket_connection_s* connection, + void* user_data); + + static void OnSocketReceivedData(bt_socket_received_data_s* data, + void* user_data); + + static void InvokeSocketOnMessageEvent(int id); + + static void InvokeSocketOnCloseEvent(int id); + + bool is_visible_; + bool is_powered_; + bool is_initialized_; + bool user_request_list_ [STOP_DISCOVERY + 1]; + double user_request_callback_ [STOP_DISCOVERY + 1]; + + bool requested_powered_; + bt_adapter_visibility_mode_e requested_visibility_; + std::string requested_name_; + + picojson::array discovered_devices_; + std::set discovered_addresses_; + std::set disappeared_addresses_; + + struct ConnectionRequest { + std::string uuid_; + double callback_handle_; + }; + + typedef std::shared_ptr ConnectionRequestPtr; + typedef std::multimap ConnectionRequestMap; + + ConnectionRequestMap connection_requests_; + + typedef std::list ConnectedSocketList; + + ConnectedSocketList connected_sockets_; + + typedef std::pair BluetoothServicePair; //registered socket - connection state + typedef std::map RegisteredUuidMap; + + RegisteredUuidMap registered_uuids_; + + std::unordered_map> socket_data_; +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_ADAPTER_H_ diff --git a/src/bluetooth/bluetooth_api.js b/src/bluetooth/bluetooth_api.js new file mode 100644 index 00000000..5812834c --- /dev/null +++ b/src/bluetooth/bluetooth_api.js @@ -0,0 +1,1425 @@ +//@ sourceURL=bluetooth_api.js + +// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +var T = xwalk.utils.type; +var Converter = xwalk.utils.converter; +var AV = xwalk.utils.validator; + +var native = new xwalk.utils.NativeManager(extension); + +// class BluetoothClassDeviceMajor ///////////////////////////////////////// +var BluetoothClassDeviceMajor = function() { + Object.defineProperties(this, { + MISC: {value: 0x00, writable: false, enumerable: true}, + COMPUTER: {value: 0x01, writable: false, enumerable: true}, + PHONE: {value: 0x02, writable: false, enumerable: true}, + NETWORK: {value: 0x03, writable: false, enumerable: true}, + AUDIO_VIDEO: {value: 0x04, writable: false, enumerable: true}, + PERIPHERAL: {value: 0x05, writable: false, enumerable: true}, + IMAGING: {value: 0x06, writable: false, enumerable: true}, + WEARABLE: {value: 0x07, writable: false, enumerable: true}, + TOY: {value: 0x08, writable: false, enumerable: true}, + HEALTH: {value: 0x09, writable: false, enumerable: true}, + UNCATEGORIZED: {value: 0x1F, writable: false, enumerable: true} + }); +}; + +// class BluetoothClassDeviceMinor ///////////////////////////////////////// +var BluetoothClassDeviceMinor = function() { + Object.defineProperties(this, { + COMPUTER_UNCATEGORIZED: {value: 0x00, writable: false, enumerable: true}, + COMPUTER_DESKTOP: {value: 0x01, writable: false, enumerable: true}, + COMPUTER_SERVER: {value: 0x02, writable: false, enumerable: true}, + COMPUTER_LAPTOP: {value: 0x03, writable: false, enumerable: true}, + COMPUTER_HANDHELD_PC_OR_PDA: {value: 0x04, writable: false, enumerable: true}, + COMPUTER_PALM_PC_OR_PDA: {value: 0x05, writable: false, enumerable: true}, + COMPUTER_WEARABLE: {value: 0x06, writable: false, enumerable: true}, + + PHONE_UNCATEGORIZED: {value: 0x00, writable: false, enumerable: true}, + PHONE_CELLULAR: {value: 0x01, writable: false, enumerable: true}, + PHONE_CORDLESS: {value: 0x02, writable: false, enumerable: true}, + PHONE_SMARTPHONE: {value: 0x03, writable: false, enumerable: true}, + PHONE_MODEM_OR_GATEWAY: {value: 0x04, writable: false, enumerable: true}, + PHONE_ISDN: {value: 0x05, writable: false, enumerable: true}, + + AV_UNRECOGNIZED: {value: 0x00, writable: false, enumerable: true}, + AV_WEARABLE_HEADSET: {value: 0x01, writable: false, enumerable: true}, + AV_HANDSFREE: {value: 0x02, writable: false, enumerable: true}, + AV_MICROPHONE: {value: 0x04, writable: false, enumerable: true}, + AV_LOUDSPEAKER: {value: 0x05, writable: false, enumerable: true}, + AV_HEADPHONES: {value: 0x06, writable: false, enumerable: true}, + AV_PORTABLE_AUDIO: {value: 0x07, writable: false, enumerable: true}, + AV_CAR_AUDIO: {value: 0x08, writable: false, enumerable: true}, + AV_SETTOP_BOX: {value: 0x09, writable: false, enumerable: true}, + AV_HIFI: {value: 0x0A, writable: false, enumerable: true}, + AV_VCR: {value: 0x0B, writable: false, enumerable: true}, + AV_VIDEO_CAMERA: {value: 0x0C, writable: false, enumerable: true}, + AV_CAMCORDER: {value: 0x0D, writable: false, enumerable: true}, + AV_MONITOR: {value: 0x0E, writable: false, enumerable: true}, + AV_DISPLAY_AND_LOUDSPEAKER: {value: 0x0F, writable: false, enumerable: true}, + AV_VIDEO_CONFERENCING: {value: 0x10, writable: false, enumerable: true}, + AV_GAMING_TOY: {value: 0x12, writable: false, enumerable: true}, + + PERIPHERAL_UNCATEGORIZED: {value: 0x00, writable: false, enumerable: true}, + PERIPHERAL_KEYBOARD: {value: 0x10, writable: false, enumerable: true}, + PERIPHERAL_POINTING_DEVICE: {value: 0x20, writable: false, enumerable: true}, + PERIPHERAL_KEYBOARD_AND_POINTING_DEVICE: { + value: 0x30, + writable: false, + enumerable: true + }, + PERIPHERAL_JOYSTICK: {value: 0x01, writable: false, enumerable: true}, + PERIPHERAL_GAMEPAD: {value: 0x02, writable: false, enumerable: true}, + PERIPHERAL_REMOTE_CONTROL: {value: 0x03, writable: false, enumerable: true}, + PERIPHERAL_SENSING_DEVICE: {value: 0x04, writable: false, enumerable: true}, + PERIPHERAL_DEGITIZER_TABLET: {value: 0x05, writable: false, enumerable: true}, + PERIPHERAL_CARD_READER: {value: 0x06, writable: false, enumerable: true}, + PERIPHERAL_DIGITAL_PEN: {value: 0x07, writable: false, enumerable: true}, + PERIPHERAL_HANDHELD_SCANNER: {value: 0x08, writable: false, enumerable: true}, + PERIPHERAL_HANDHELD_INPUT_DEVICE: {value: 0x09, writable: false, enumerable: true}, + + IMAGING_UNCATEGORIZED: {value: 0x00, writable: false, enumerable: true}, + IMAGING_DISPLAY: {value: 0x04, writable: false, enumerable: true}, + IMAGING_CAMERA: {value: 0x08, writable: false, enumerable: true}, + IMAGING_SCANNER: {value: 0x10, writable: false, enumerable: true}, + IMAGING_PRINTER: {value: 0x20, writable: false, enumerable: true}, + + WEARABLE_WRITST_WATCH: {value: 0x01, writable: false, enumerable: true}, + WEARABLE_PAGER: {value: 0x02, writable: false, enumerable: true}, + WEARABLE_JACKET: {value: 0x03, writable: false, enumerable: true}, + WEARABLE_HELMET: {value: 0x04, writable: false, enumerable: true}, + WEARABLE_GLASSES: {value: 0x05, writable: false, enumerable: true}, + + TOY_ROBOT: {value: 0x01, writable: false, enumerable: true}, + TOY_VEHICLE: {value: 0x02, writable: false, enumerable: true}, + TOY_DOLL: {value: 0x03, writable: false, enumerable: true}, + TOY_CONTROLLER: {value: 0x04, writable: false, enumerable: true}, + TOY_GAME: {value: 0x05, writable: false, enumerable: true}, + + HEALTH_UNDEFINED: {value: 0x00, writable: false, enumerable: true}, + HEALTH_BLOOD_PRESSURE_MONITOR: {value: 0x01, writable: false, enumerable: true}, + HEALTH_THERMOMETER: {value: 0x02, writable: false, enumerable: true}, + HEALTH_WEIGHING_SCALE: {value: 0x03, writable: false, enumerable: true}, + HEALTH_GLUCOSE_METER: {value: 0x04, writable: false, enumerable: true}, + HEALTH_PULSE_OXIMETER: {value: 0x05, writable: false, enumerable: true}, + HEALTH_PULSE_RATE_MONITOR: {value: 0x06, writable: false, enumerable: true}, + HEALTH_DATA_DISPLAY: {value: 0x07, writable: false, enumerable: true}, + HEALTH_STEP_COUNTER: {value: 0x08, writable: false, enumerable: true}, + HEALTH_BODY_COMPOSITION_ANALYZER: {value: 0x09, writable: false, enumerable: true}, + HEALTH_PEAK_FLOW_MONITOR: {value: 0x0A, writable: false, enumerable: true}, + HEALTH_MEDICATION_MONITOR: {value: 0x0B, writable: false, enumerable: true}, + HEALTH_KNEE_PROSTHESIS: {value: 0x0C, writable: false, enumerable: true}, + HEALTH_ANKLE_PROSTHESIS: {value: 0x0D, writable: false, enumerable: true} + }); +}; + +// class BluetoothClassDeviceService /////////////////////////////////////// +var BluetoothClassDeviceService = function() { + Object.defineProperties(this, { + LIMITED_DISCOVERABILITY: {value: 0x0001, writable: false, enumerable: true}, + POSITIONING: {value: 0x0008, writable: false, enumerable: true}, + NETWORKING: {value: 0x0010, writable: false, enumerable: true}, + RENDERING: {value: 0x0020, writable: false, enumerable: true}, + CAPTURING: {value: 0x0040, writable: false, enumerable: true}, + OBJECT_TRANSFER: {value: 0x0080, writable: false, enumerable: true}, + AUDIO: {value: 0x0100, writable: false, enumerable: true}, + TELEPHONY: {value: 0x0200, writable: false, enumerable: true}, + INFORMATION: {value: 0x0400, writable: false, enumerable: true} + }); +}; + +// class BluetoothClass //////////////////////////////////////////////////// +var BluetoothClass = function(data) { + Object.defineProperties(this, { + major : {value: data.major, writable: false, enumerable: true}, + minor : {value: data.minor, writable: false, enumerable: true}, + services : {value: data.services, writable: false, enumerable: true} + }); +}; + +var _PRIVILEGE_BLUETOOTH_GAP = 'http://tizen.org/privilege/bluetooth.gap'; + +BluetoothClass.prototype.hasService = function() { + console.log('Entered BluetoothClass.hasService()'); + + var result = native.callSync('Bluetooth_checkPrivilege', {privilege : _PRIVILEGE_BLUETOOTH_GAP}); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } else { + var args = AV.validateMethod(arguments, [ + { + name : 'service', + type : AV.Types.UNSIGNED_LONG + } + ]); + + var size = this.services.length; + for (var i = 0; i < size; i++) { + if (this.services[i] === args.service) { + return true; + } + } + return false; + } +}; + +// class BluetoothSocket //////////////////////////////////////////////////// +var _BLUETOOTH_SOCKET_STATE_CLOSED = 'CLOSED'; + +function BluetoothSocketListeners() { + var that = this; + this.socketCallback = function (data) { + var event = data; + var socket = that.sockets[event.id]; + + if (socket) { + if ('onclose' === event.event) { + // no more events + that.removeListener(event.id); + // change state + Object.defineProperty(socket, 'state', {value : _BLUETOOTH_SOCKET_STATE_CLOSED}); + } + + var callback = socket[event.event]; + if (T.isFunction(callback)) { + callback(); + } + } else { + console.log('Received event for an unknown socket: ' + event.id); + } + }; +} + +BluetoothSocketListeners.prototype.sockets = {}; + +BluetoothSocketListeners.prototype.addListener = function(socket) { + if (T.isEmptyObject(this.sockets)) { + native.addListener('BLUETOOTH_SOCKET_STATE_CHANGED', this.socketCallback); + } + + this.sockets[socket._id] = socket; +}; + +BluetoothSocketListeners.prototype.removeListener = function(id) { + delete this.sockets[id]; + + if (T.isEmptyObject(this.sockets)) { + native.removeListener('BLUETOOTH_SOCKET_STATE_CHANGED', this.socketCallback); + } +}; + +var _bluetoothSocketListeners = new BluetoothSocketListeners(); + +var BluetoothSocket = function(data) { + Object.defineProperties(this, { + uuid : {value: data.uuid, writable: false, enumerable: true}, + state : {value: data.state, writable: false, enumerable: true, configurable: true}, + peer : {value: new BluetoothDevice(data.peer), writable: false, enumerable: true}, + onmessage : {value: null, writable: true, enumerable: true}, + onclose : {value: null, writable: true, enumerable: true}, + _id : {value: data.id, writable: false, enumerable: false} + }); + + _bluetoothSocketListeners.addListener(this); +}; + +BluetoothSocket.prototype.writeData = function() { + console.log('Entered BluetoothSocket.writeData()'); + + var args = AV.validateMethod(arguments, [ + { + name : 'data', + type : AV.Types.ARRAY, + values : AV.Types.BYTE + } + ]); + + var callArgs = { + id : this._id, + data : args.data + }; + + var result = native.callSync('BluetoothSocket_writeData', callArgs); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } else { + return native.getResultObject(result); + } +}; + +BluetoothSocket.prototype.readData = function() { + console.log('Entered BluetoothSocket.readData()'); + + var callArgs = { + id : this._id + }; + + var result = native.callSync('BluetoothSocket_readData', callArgs); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } else { + return native.getResultObject(result); + } +}; + +BluetoothSocket.prototype.close = function() { + console.log('Entered BluetoothSocket.close()'); + + if (_BLUETOOTH_SOCKET_STATE_CLOSED !== this.state) { + var callArgs = { + id : this._id + }; + + var result = native.callSync('BluetoothSocket_close', callArgs); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } + + // change state + Object.defineProperty(this, 'state', { value : _BLUETOOTH_SOCKET_STATE_CLOSED }); + } +}; + +// class BluetoothDevice //////////////////////////////////////////////////// +var BluetoothDevice = function(data) { + var self = this; + function _getter(field) { + var callArgs = {}; + + callArgs.address = self.address; + callArgs.field = field; + + var result = native.callSync('BluetoothDevice_getBoolValue', callArgs); + + if (native.isFailure(result)) { + return false; + } else { + return native.getResultObject(result); + } + } + + function isBondedGetter() { + return _getter('isBonded'); + } + + function isTrustedGetter() { + return _getter('isTrusted'); + } + + function isConnectedGetter() { + return _getter('isConnected'); + } + + Object.defineProperties(this, { + name : {value: data.name, writable: false, enumerable: true}, + address : {value: data.address, writable: false, enumerable: true}, + deviceClass : {value: new BluetoothClass(data.deviceClass), + writable: false, + enumerable: true}, + isBonded : { + enumerable: true, + set : function(){}, + get : isBondedGetter + }, + isTrusted : { + enumerable: true, + set : function(){}, + get : isTrustedGetter + }, + isConnected : { + enumerable: true, + set : function(){}, + get : isConnectedGetter + }, + uuids : {value: data.uuids, writable: false, enumerable: true} + }); +}; + +BluetoothDevice.prototype.connectToServiceByUUID = function() { + console.log('Entered BluetoothDevice.connectToServiceByUUID()'); + + var args = AV.validateMethod(arguments, [ + { + name : 'uuid', + type : AV.Types.STRING + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + address : this.address, + uuid : args.uuid + }; + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + args.successCallback(new BluetoothSocket(native.getResultObject(result))); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothDevice_connectToServiceByUUID', callArgs, callback); +}; + +// class BluetoothServiceHandler //////////////////////////////////////////////////// +function BluetoothServiceListeners() { + var that = this; + this.serviceCallback = function (data) { + var e = data; + var service = that.services[e.uuid]; + var result = new BluetoothSocket(e); + + if (service) { + console.log(service); + service.onconnect(result); + } + }; +} + +BluetoothServiceListeners.prototype.services = {}; + +BluetoothServiceListeners.prototype.addListener = function(service) { + if (T.isEmptyObject(this.services)) { + native.addListener('BLUETOOTH_SERVICE_ONCONNECT', this.serviceCallback); + } + + this.services[service.uuid] = service; +}; + +BluetoothServiceListeners.prototype.removeListener = function(uuid) { + delete this.services[uuid]; + + if (T.isEmptyObject(this.services)) { + native.removeListener('BLUETOOTH_SERVICE_ONCONNECT', this.serviceCallback); + } +}; + +var _bluetoothServiceListeners = new BluetoothServiceListeners(); + +var BluetoothServiceHandler = function(data) { + function isConnectedGetter() { + var callArgs = { + uuid : this.uuid + }; + + var result = native.callSync('BluetoothAdapter_isServiceConnected', { uuid : this.uuid }); + + if (native.isFailure(result)) { + return false; + } else { + return native.getResultObject(result); + } + } + + Object.defineProperties(this, { + uuid : {value: data.uuid, writable: false, enumerable: true}, + name : {value: data.name, writable: false, enumerable: true}, + isConnected : { + enumerable: true, + set : function(){}, + get : isConnectedGetter + }, + onconnect : {value: null, writable: true, enumerable: true} + }); + + _bluetoothServiceListeners.addListener(this); +}; + +BluetoothServiceHandler.prototype.unregister = function() { + console.log('Entered BluetoothServiceHandler.unregister()'); + var args = AV.validateMethod(arguments, [ + { + name : 'successCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + uuid : this.uuid + }; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + native.callIfPossible(args.successCallback); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothServiceHandler_unregister', callArgs, callback); + + _bluetoothServiceListeners.removeListener(this.uuid); +}; + +// class BluetoothHealthApplication //////////////////////////////////////////////////// +function BluetoothHealthApplicationListeners() { + var that = this; + this.appCallback = function (data) { + var event = data; + var app = that.apps[event.id]; + + if (app) { + var callback = app[event.event]; + if (T.isFunction(callback)) { + var param; + switch (event.event) { + case 'onconnect': + param = new BluetoothHealthChannel(native.getResultObject(event)); + break; + + default: + console.log('Unknown event: ' + event.event); + break; + } + callback(param); + } + } else { + console.log('Received event for an unknown application: ' + event.id); + } + }; +} + +BluetoothHealthApplicationListeners.prototype.apps = {}; + +BluetoothHealthApplicationListeners.prototype.addListener = function(app) { + if (T.isEmptyObject(this.apps)) { + native.addListener('BLUETOOTH_HEALTH_APPLICATION_CHANGED', this.appCallback); + } + + this.apps[app._id] = app; +}; + +BluetoothHealthApplicationListeners.prototype.removeListener = function(id) { + delete this.apps[id]; + + if (T.isEmptyObject(this.apps)) { + native.removeListener('BLUETOOTH_HEALTH_APPLICATION_CHANGED', this.appCallback); + } +}; + +var _bluetoothHealthApplicationListeners = new BluetoothHealthApplicationListeners(); + +var BluetoothHealthApplication = function(data) { + Object.defineProperties(this, { + dataType : {value: data.dataType, writable: false, enumerable: true}, + name : {value: data.name, writable: false, enumerable: true}, + onconnect : {value: null, writable: true, enumerable: true}, + _id : {value: data._id, writable: false, enumerable: false} + }); + + _bluetoothHealthApplicationListeners.addListener(this); +}; + +BluetoothHealthApplication.prototype.unregister = function() { + console.log('Entered BluetoothHealthApplication.unregister()'); + var args = AV.validateMethod(arguments, [ + { + name : 'successCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = {id : this._id}; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + native.callIfPossible(args.successCallback); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothHealthApplication_unregister', callArgs, callback); + + _bluetoothHealthApplicationListeners.removeListener(this._id); +}; + +// class BluetoothProfileHandler //////////////////////////////////////////////////// +var _BluetoothProfileType = { + HEALTH : 'HEALTH' +}; + +var BluetoothProfileHandler = function(data) { + if (data) { + Object.defineProperties(this, { + profileType : {value: data.profileType, writable: false, enumerable: true} + }); + } +}; + +// class BluetoothHealthProfileHandler //////////////////////////////////////////////////// +var BluetoothHealthProfileHandler = function(data) { + BluetoothProfileHandler.call(this, data); +}; + +BluetoothHealthProfileHandler.prototype = new BluetoothProfileHandler(); + +BluetoothHealthProfileHandler.prototype.constructor = BluetoothProfileHandler; + +BluetoothHealthProfileHandler.prototype.registerSinkApplication = function() { + console.log('Entered BluetoothHealthProfileHandler.registerSinkApplication()'); + + var args = AV.validateMethod(arguments, [ + { + name : 'dataType', + type : AV.Types.LONG // there's no short type + }, + { + name : 'name', + type : AV.Types.STRING + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + dataType : args.dataType, + name : args.name + }; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + args.successCallback(new BluetoothHealthApplication(native.getResultObject(result))); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothHealthProfileHandler_registerSinkApp', callArgs, callback); +}; + +BluetoothHealthProfileHandler.prototype.connectToSource = function() { + console.log('Entered BluetoothHealthProfileHandler.connectToSource()'); + + var args = AV.validateMethod(arguments, [ + { + name : 'peer', + type : AV.Types.PLATFORM_OBJECT, + values : BluetoothDevice + }, + { + name : 'application', + type : AV.Types.PLATFORM_OBJECT, + values : BluetoothHealthApplication + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + address : args.peer.address, + appId : args.application._id + }; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + var channel = native.getResultObject(result); + channel.peer = args.peer; + channel.appId = args.application._id; + args.successCallback(new BluetoothHealthChannel(channel)); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothHealthProfileHandler_connectToSource', callArgs, callback); +}; + +// class BluetoothHealthChannel //////////////////////////////////////////////////// +var BluetoothHealthChannel = function(data) { + Object.defineProperties(this, { + peer : {value: data.peer, writable: false, enumerable: true}, + channelType : {value: data.channelType, writable: false, enumerable: true}, + application : { + value: _bluetoothHealthApplicationListeners.apps[data.appId], + writable: false, + enumerable: true + }, + isConnected : { + value: data.isConnected, + writable: false, + enumerable: true, + configurable: true}, + _id : {value: data._id, writable: false, enumerable: false} + }); +}; + +BluetoothHealthChannel.prototype.close = function() { + console.log('Entered BluetoothHealthChannel.close()'); + + if (this.isConnected) { + var callArgs = { + channel : this._id, + address : this.peer.address + }; + + var result = native.callSync('BluetoothHealthChannel_close', callArgs); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } + + Object.defineProperty(this, 'isConnected', { value : false }); + } +}; + +BluetoothHealthChannel.prototype.sendData = function() { + console.log('Entered BluetoothHealthChannel.sendData()'); + + var args = AV.validateMethod(arguments, [ + { + name : 'data', + type : AV.Types.ARRAY, + values : AV.Types.BYTE + } + ]); + + var callArgs = { + channel : this._id, + data : args.data + }; + + var result = native.callSync('BluetoothHealthChannel_sendData', callArgs); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } else { + return native.getResultObject(result); + } +}; + +var _healthListeners = {}; + +function _BluetoothHealthChannelChangeCallback(event) { + var e = event; + var callback = _healthListeners[e.id]; + var d; + + switch (e.event) { + case 'onmessage': + d = e.data; + break; + + case 'onclose': + break; + + default: + console.log('Unknown mode: ' + e.event); + return; + } + + if (callback[e.event]) { + callback[e.event](d); + } +} + +var _PRIVILEGE_BLUETOOTH_HEALTH = 'http://tizen.org/privilege/bluetooth.health'; + +BluetoothHealthChannel.prototype.setListener = function() { + console.log('Entered BluetoothHealthChannel.setListener()'); + var args = AV.validateMethod(arguments, [ + { + name : 'changeCallback', + type : AV.Types.LISTENER, + values : ['onmessage', 'onclose'] + } + ]); + + var result = native.callSync('Bluetooth_checkPrivilege', {privilege : _PRIVILEGE_BLUETOOTH_HEALTH}); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } else { + if (T.isEmptyObject(_healthListeners)) { + native.addListener('BluetoothHealthChannelChangeCallback', + _BluetoothHealthChannelChangeCallback); + } + _healthListeners[this._id] = args.changeCallback; + } +}; + +BluetoothHealthChannel.prototype.unsetListener = function() { + console.log('Entered BluetoothHealthChannel.unsetListener ()'); + + var result = native.callSync('Bluetooth_checkPrivilege', {privilege : _PRIVILEGE_BLUETOOTH_HEALTH}); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } else { + delete _healthListeners[this._id]; + + if (T.isEmptyObject(_healthListeners)) { + native.removeListener('BluetoothHealthChannelChangeCallback', + _BluetoothHealthChannelChangeCallback); + } + } +}; + +// class BluetoothAdapter //////////////////////////////////////////////////// +var BluetoothAdapter = function() { + function nameGetter() { + var result = native.callSync('BluetoothAdapter_getName', {}); + + if (native.isFailure(result)) { + return ''; + } else { + return native.getResultObject(result); + } + } + + function addressGetter() { + var result = native.callSync('BluetoothAdapter_getAddress', {}); + + if (native.isFailure(result)) { + return ''; + } else { + return native.getResultObject(result); + } + } + + function poweredGetter() { + var result = native.callSync('BluetoothAdapter_getPowered', {}); + + if (native.isFailure(result)) { + return false; + } else { + return native.getResultObject(result); + } + } + + function visibleGetter() { + var result = native.callSync('BluetoothAdapter_getVisible', {}); + + if (native.isFailure(result)) { + return false; + } else { + return native.getResultObject(result); + } + } + + Object.defineProperties(this, { + name : { + enumerable: true, + set : function(){}, + get : nameGetter + }, + address : { + enumerable: true, + set : function(){}, + get : addressGetter + }, + powered : { + enumerable: true, + set : function(){}, + get : poweredGetter + }, + visible : { + enumerable: true, + set : function(){}, + get : visibleGetter + } + }); +}; + +BluetoothAdapter.prototype.setName = function() { + console.log('Entered BluetoothAdapter.setName()'); + var args = AV.validateMethod(arguments, [ + { + name : 'name', + type : AV.Types.STRING + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + name : args.name + }; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + native.callIfPossible(args.successCallback); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_setName', callArgs, callback); +}; + +BluetoothAdapter.prototype.setPowered = function() { + console.log('Entered BluetoothAdapter.setPowered()'); + var args = AV.validateMethod(arguments, [ + { + name : 'powered', + type : AV.Types.BOOLEAN + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + powered : args.powered + }; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + native.callIfPossible(args.successCallback); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_setPowered', callArgs, callback); +}; + +BluetoothAdapter.prototype.setVisible = function() { + console.log('Entered BluetoothAdapter.setVisible()'); + var args = AV.validateMethod(arguments, [ + { + name : 'visible', + type : AV.Types.BOOLEAN + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + }, + { + name : 'timeout', + type : AV.Types.UNSIGNED_LONG, + optional : true, + nullable : true + } + ]); + + var callArgs = { + visible : args.visible, + }; + + if (args.visible === true) { + if (T.isNullOrUndefined(args.timeout)) { + callArgs.timeout = 0; + } else { + callArgs.timeout = args.timeout > 65535 ? 180 : args.timeout; + } + } + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + native.callIfPossible(args.successCallback); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_setVisible', callArgs, callback); +}; + +var _listener; + +function _BluetoothAdapterChangeCallback(event) { + console.log('_BluetoothAdapterChangeCallback'); + + var e = event; + var d; + + switch (e.action) { + case 'onstatechanged': + d = e.powered; + break; + + case 'onnamechanged': + d = e.name; + break; + + case 'onvisibilitychanged': + d = e.visible; + break; + + default: + console.log('Unknown mode: ' + e.action); + return; + } + + if (_listener[e.action]) { + _listener[e.action](d); + } +} + +BluetoothAdapter.prototype.setChangeListener = function() { + console.log('Entered BluetoothAdapter.setChangeListener()'); + var args = AV.validateMethod(arguments, [ + { + name : 'changeCallback', + type : AV.Types.LISTENER, + values : ['onstatechanged', 'onnamechanged', 'onvisibilitychanged'] + } + ]); + + if (T.isNullOrUndefined(_listener)) { + native.addListener('BluetoothAdapterChangeCallback', _BluetoothAdapterChangeCallback); + } + _listener = args.changeCallback; +}; + +BluetoothAdapter.prototype.unsetChangeListener = function() { + console.log('Entered BluetoothAdapter.unsetChangeListener()'); + if (!T.isNullOrUndefined(_listener)) { + native.removeListener('BluetoothAdapterChangeCallback', _BluetoothAdapterChangeCallback); + _listener = undefined; + } +}; + +var _discoverDevicesSuccessCallback; +var _discoverDevicesErrorCallback; + +function _BluetoothDiscoverDevicesSuccessCallback(event) { + var e = event; + var d = null; + + switch (e.action) { + case 'onstarted': + break; + + case 'ondevicefound': + d = new BluetoothDevice(e.data); + break; + + case 'ondevicedisappeared': + d = e.data; + break; + + case 'onfinished': + var result = e.data; + d = []; + result.forEach(function (data) { + d.push(new BluetoothDevice(data)); + }); + + //remove listeners after discovering + native.removeListener('BluetoothDiscoverDevicesSuccessCallback', + _BluetoothDiscoverDevicesSuccessCallback); + native.removeListener('BluetoothDiscoverDevicesErrorCallback', + _BluetoothDiscoverDevicesErrorCallback); + break; + + default: + console.log('Unknown mode: ' + e.action); + return; + } + + if (_discoverDevicesSuccessCallback[e.action]) { + _discoverDevicesSuccessCallback[e.action](d); + } +} + +function _BluetoothDiscoverDevicesErrorCallback(event) { + var e = event; + setTimeout(function() { + native.callIfPossible(_discoverDevicesErrorCallback, native.getErrorObject(e)); + }, 0); +} + +BluetoothAdapter.prototype.discoverDevices = function() { + console.log('Entered BluetoothAdapter.discoverDevices()'); + var args = AV.validateMethod(arguments, [ + { + name : 'successCallback', + type : AV.Types.LISTENER, + values : ['onstarted', 'ondevicefound', 'ondevicedisappeared', 'onfinished'] + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + _discoverDevicesSuccessCallback = args.successCallback; + _discoverDevicesErrorCallback = args.errorCallback; + native.addListener('BluetoothDiscoverDevicesSuccessCallback', + _BluetoothDiscoverDevicesSuccessCallback); + native.addListener('BluetoothDiscoverDevicesErrorCallback', + _BluetoothDiscoverDevicesErrorCallback); + + var result = native.callSync('BluetoothAdapter_discoverDevices', {}); + + if (native.isFailure(result)) { + native.removeListener('BluetoothDiscoverDevicesSuccessCallback', + _BluetoothDiscoverDevicesSuccessCallback); + native.removeListener('BluetoothDiscoverDevicesErrorCallback', + _BluetoothDiscoverDevicesErrorCallback); + throw native.getErrorObject(result); + } +}; + +BluetoothAdapter.prototype.stopDiscovery = function() { + console.log('Entered BluetoothAdapter.stopDiscovery()'); + var args = AV.validateMethod(arguments, [ + { + name : 'successCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + native.callIfPossible(args.successCallback); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_stopDiscovery', {}, callback); +}; + +BluetoothAdapter.prototype.getKnownDevices = function() { + console.log('Entered BluetoothAdapter.getKnownDevices()'); + var args = AV.validateMethod(arguments, [ + { + name : 'successCallback', + type : AV.Types.FUNCTION + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + var r = native.getResultObject(result).devices; + var devices = []; + r.forEach(function (data) { + devices.push(new BluetoothDevice(data)); + }); + args.successCallback(devices); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_getKnownDevices', {}, callback); +}; + +BluetoothAdapter.prototype.getDevice = function() { + console.log('Entered BluetoothAdapter.getDevice()'); + var args = AV.validateMethod(arguments, [ + { + name : 'address', + type : AV.Types.STRING + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + args.successCallback(new BluetoothDevice(native.getResultObject(result))); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_getDevice', {address : args.address}, callback); +}; + +BluetoothAdapter.prototype.createBonding = function() { + console.log('Entered BluetoothAdapter.createBonding()'); + var args = AV.validateMethod(arguments, [ + { + name : 'address', + type : AV.Types.STRING + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION, + optional : false, + nullable : false + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + address : args.address + }; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + args.successCallback(new BluetoothDevice(native.getResultObject(result))); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_createBonding', callArgs, callback); +}; + +BluetoothAdapter.prototype.destroyBonding = function() { + console.log('Entered BluetoothAdapter.destroyBonding()'); + var args = AV.validateMethod(arguments, [ + { + name : 'address', + type : AV.Types.STRING + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + address : args.address + }; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + native.callIfPossible(args.successCallback); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_destroyBonding', callArgs, callback); +}; + +BluetoothAdapter.prototype.registerRFCOMMServiceByUUID = function() { + console.log('Entered BluetoothAdapter.registerRFCOMMServiceByUUID()'); + var args = AV.validateMethod(arguments, [ + { + name : 'uuid', + type : AV.Types.STRING + }, + { + name : 'name', + type : AV.Types.STRING + }, + { + name : 'successCallback', + type : AV.Types.FUNCTION, + }, + { + name : 'errorCallback', + type : AV.Types.FUNCTION, + optional : true, + nullable : true + } + ]); + + var callArgs = { + uuid : args.uuid, + name : args.name + }; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + // if registration was finished with success create BluetoothServiceHandler + // with parameters passed to this function (uuid and name). + args.successCallback(new BluetoothServiceHandler(callArgs)); + } + }; + + // native.call does not inform if call results in failure + // TODO: what to do in this case? + native.call('BluetoothAdapter_registerRFCOMMServiceByUUID', callArgs, callback); +}; + +BluetoothAdapter.prototype.getBluetoothProfileHandler = function() { + console.log('Entered BluetoothAdapter.getBluetoothProfileHandler()'); + + var args = AV.validateMethod(arguments, [ + { + name : 'profileType', + type : AV.Types.ENUM, + values : T.getValues(_BluetoothProfileType) + } + ]); + + var callArgs = {profileType : args.profileType}; + + var result = native.callSync('BluetoothAdapter_getBluetoothProfileHandler', callArgs); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } else { + switch (args.profileType) { + case _BluetoothProfileType.HEALTH: + return new BluetoothHealthProfileHandler(callArgs); + + default: + throw new tizen.WebAPIException('NotSupportedError', 'Profile ' + args.profileType + ' is not supported.'); + } + } +}; + +// class BluetoothManager //////////////////////////////////////////////////// +var BluetoothManager = function() { + Object.defineProperties(this, { + deviceMajor : { + value: new BluetoothClassDeviceMajor(), + writable: false, + enumerable: true + }, + deviceMinor : { + value: new BluetoothClassDeviceMinor(), + writable: false, + enumerable: true + }, + deviceService : { + value: new BluetoothClassDeviceService(), + writable: false, + enumerable: true + } + }); +}; + +BluetoothManager.prototype.getDefaultAdapter = function() { + console.log('Entered BluetoothManager.getDefaultAdapter()'); + + var result = native.callSync('Bluetooth_checkPrivilege', {privilege : _PRIVILEGE_BLUETOOTH_GAP}); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } else { + return new BluetoothAdapter(); + } +}; + +// exports /////////////////////////////////////////////////////////////////// +exports = new BluetoothManager(); diff --git a/src/bluetooth/bluetooth_class.cc b/src/bluetooth/bluetooth_class.cc new file mode 100644 index 00000000..3addc6ab --- /dev/null +++ b/src/bluetooth/bluetooth_class.cc @@ -0,0 +1,157 @@ +/* + * 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_class.h" + +#include + +namespace extension { +namespace bluetooth { + +std::map g_major_enum_map = { + {BT_MAJOR_DEVICE_CLASS_MISC, 0x00}, + {BT_MAJOR_DEVICE_CLASS_COMPUTER, 0x01}, + {BT_MAJOR_DEVICE_CLASS_PHONE, 0x02}, + {BT_MAJOR_DEVICE_CLASS_LAN_NETWORK_ACCESS_POINT, 0x03}, + {BT_MAJOR_DEVICE_CLASS_AUDIO_VIDEO, 0x04}, + {BT_MAJOR_DEVICE_CLASS_PERIPHERAL, 0x05}, + {BT_MAJOR_DEVICE_CLASS_IMAGING, 0x06}, + {BT_MAJOR_DEVICE_CLASS_WEARABLE, 0x07}, + {BT_MAJOR_DEVICE_CLASS_TOY, 0x08}, + {BT_MAJOR_DEVICE_CLASS_HEALTH, 0x09}, + {BT_MAJOR_DEVICE_CLASS_UNCATEGORIZED, 0x1F} +}; + +std::map g_minor_enum_map = { + {BT_MINOR_DEVICE_CLASS_COMPUTER_UNCATEGORIZED, 0x00}, + {BT_MINOR_DEVICE_CLASS_COMPUTER_DESKTOP_WORKSTATION , 0x01}, + {BT_MINOR_DEVICE_CLASS_COMPUTER_SERVER_CLASS , 0x02}, + {BT_MINOR_DEVICE_CLASS_COMPUTER_LAPTOP , 0x03}, + {BT_MINOR_DEVICE_CLASS_COMPUTER_HANDHELD_PC_OR_PDA , 0x04}, + {BT_MINOR_DEVICE_CLASS_COMPUTER_PALM_SIZED_PC_OR_PDA, 0x5}, + {BT_MINOR_DEVICE_CLASS_COMPUTER_WEARABLE_COMPUTER , 0x06}, + {BT_MINOR_DEVICE_CLASS_PHONE_UNCATEGORIZED , 0x00}, + {BT_MINOR_DEVICE_CLASS_PHONE_CELLULAR , 0x01}, + {BT_MINOR_DEVICE_CLASS_PHONE_CORDLESS , 0x02}, + {BT_MINOR_DEVICE_CLASS_PHONE_SMART_PHONE , 0x03}, + {BT_MINOR_DEVICE_CLASS_PHONE_WIRED_MODEM_OR_VOICE_GATEWAY , 0x04}, + {BT_MINOR_DEVICE_CLASS_PHONE_COMMON_ISDN_ACCESS , 0x05}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_UNCATEGORIZED , 0x00}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_WEARABLE_HEADSET , 0x01}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_HANDS_FREE , 0x02}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_MICROPHONE , 0x04}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_LOUDSPEAKER , 0x05}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_HEADPHONES , 0x06}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_PORTABLE_AUDIO , 0x07}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_CAR_AUDIO , 0x08}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_SET_TOP_BOX , 0x09}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_HIFI_AUDIO_DEVICE , 0x0a}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_VCR , 0x0b}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_VIDEO_CAMERA , 0x0c}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_CAMCORDER , 0x0d}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_VIDEO_MONITOR , 0x0e}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_VIDEO_DISPLAY_LOUDSPEAKER , 0x0f}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_VIDEO_CONFERENCING , 0x10}, + {BT_MINOR_DEVICE_CLASS_AUDIO_VIDEO_GAMING_TOY , 0x12}, + {BT_MINOR_DEVICE_CLASS_PERIPHERA_UNCATEGORIZED , 0}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_KEY_BOARD , 0x10}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_POINTING_DEVICE , 0x20}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_COMBO_KEYBOARD_POINTING_DEVICE , 0x30}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_JOYSTICK , 0x01}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_GAME_PAD , 0x02}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_REMOTE_CONTROL , 0x03}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_SENSING_DEVICE , 0x04}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_DIGITIZER_TABLET , 0x05}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_CARD_READER , 0x06}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_DIGITAL_PEN , 0x07}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_HANDHELD_SCANNER , 0x08}, + {BT_MINOR_DEVICE_CLASS_PERIPHERAL_HANDHELD_GESTURAL_INPUT_DEVICE , 0x09}, + {BT_MINOR_DEVICE_CLASS_IMAGING_DISPLAY , 0x04}, + {BT_MINOR_DEVICE_CLASS_IMAGING_CAMERA , 0x08}, + {BT_MINOR_DEVICE_CLASS_IMAGING_SCANNER , 0x10}, + {BT_MINOR_DEVICE_CLASS_IMAGING_PRINTER, 0x20}, + {BT_MINOR_DEVICE_CLASS_WEARABLE_WRIST_WATCH , 0x01}, + {BT_MINOR_DEVICE_CLASS_WEARABLE_PAGER , 0x02}, + {BT_MINOR_DEVICE_CLASS_WEARABLE_JACKET , 0x03}, + {BT_MINOR_DEVICE_CLASS_WEARABLE_HELMET , 0x04}, + {BT_MINOR_DEVICE_CLASS_WEARABLE_GLASSES , 0x05}, + {BT_MINOR_DEVICE_CLASS_TOY_ROBOT , 0x01}, + {BT_MINOR_DEVICE_CLASS_TOY_VEHICLE , 0x02}, + {BT_MINOR_DEVICE_CLASS_TOY_DOLL_ACTION , 0x03}, + {BT_MINOR_DEVICE_CLASS_TOY_CONTROLLER , 0x04}, + {BT_MINOR_DEVICE_CLASS_TOY_GAME , 0x05}, + {BT_MINOR_DEVICE_CLASS_HEALTH_UNCATEGORIZED , 0x00}, + {BT_MINOR_DEVICE_CLASS_HEALTH_BLOOD_PRESSURE_MONITOR , 0x01}, + {BT_MINOR_DEVICE_CLASS_HEALTH_THERMOMETER , 0x02}, + {BT_MINOR_DEVICE_CLASS_HEALTH_WEIGHING_SCALE , 0x03}, + {BT_MINOR_DEVICE_CLASS_HEALTH_GLUCOSE_METER , 0x04}, + {BT_MINOR_DEVICE_CLASS_HEALTH_PULSE_OXIMETER , 0x05}, + {BT_MINOR_DEVICE_CLASS_HEALTH_HEART_PULSE_RATE_MONITOR , 0x06}, + {BT_MINOR_DEVICE_CLASS_HEALTH_DATA_DISPLAY , 0x07}, + {BT_MINOR_DEVICE_CLASS_HEALTH_STEP_COUNTER , 0x08}, + {BT_MINOR_DEVICE_CLASS_HEALTH_BODY_COMPOSITION_ANALYZER , 0x09}, + {BT_MINOR_DEVICE_CLASS_HEALTH_PEAK_FLOW_MONITOR , 0x0a}, + {BT_MINOR_DEVICE_CLASS_HEALTH_MEDICATION_MONITOR , 0x0b}, + {BT_MINOR_DEVICE_CLASS_HEALTH_KNEE_PROSTHESIS , 0x0c}, + {BT_MINOR_DEVICE_CLASS_HEALTH_ANKLE_PROSTHESIS , 0x0d} +}; + +std::map g_service_enum_map = { + {BT_MAJOR_SERVICE_CLASS_LIMITED_DISCOVERABLE_MODE, 0x0001}, + {BT_MAJOR_SERVICE_CLASS_POSITIONING, 0x0008}, + {BT_MAJOR_SERVICE_CLASS_NETWORKING, 0x0010}, + {BT_MAJOR_SERVICE_CLASS_RENDERING, 0x0020}, + {BT_MAJOR_SERVICE_CLASS_CAPTURING, 0x0040}, + {BT_MAJOR_SERVICE_CLASS_OBJECT_TRANSFER, 0x0080}, + {BT_MAJOR_SERVICE_CLASS_AUDIO, 0x0100}, + {BT_MAJOR_SERVICE_CLASS_TELEPHONY, 0x0200}, + {BT_MAJOR_SERVICE_CLASS_INFORMATION, 0x0400} +}; + +unsigned long BluetoothClass::GetMajorValue(bt_major_device_class_e major) +{ + auto iter = g_major_enum_map.find(major); + if (iter != g_major_enum_map.end()) { + return iter->second; + } + + return 0; +} + +unsigned long BluetoothClass::GetMinorValue(bt_minor_device_class_e minor) +{ + auto iter = g_minor_enum_map.find(minor); + if (iter != g_minor_enum_map.end()) { + return iter->second; + } + + return 0; +} + +std::vector BluetoothClass::getServiceValues(int serviceMask) +{ + std::vector ret; + for (auto iter = g_service_enum_map.begin(); iter != g_service_enum_map.end(); iter++) { + if (iter->first & serviceMask) { + ret.push_back(iter->second); + } + } + + return ret; +} + +} // namespace bluetooth +} // namespace extension diff --git a/src/bluetooth/bluetooth_class.h b/src/bluetooth/bluetooth_class.h new file mode 100644 index 00000000..0445835a --- /dev/null +++ b/src/bluetooth/bluetooth_class.h @@ -0,0 +1,37 @@ +/* + * 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_CLASS_H_ +#define BLUETOOTH_BLUETOOTH_CLASS_H_ + +#include + +#include + +namespace extension { +namespace bluetooth { + +class BluetoothClass { +public: + static unsigned long GetMajorValue(bt_major_device_class_e major); + static unsigned long GetMinorValue(bt_minor_device_class_e minor); + static std::vector getServiceValues(int serviceMask); +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_CLASS_H_ diff --git a/src/bluetooth/bluetooth_device.cc b/src/bluetooth/bluetooth_device.cc new file mode 100644 index 00000000..6f05aa5b --- /dev/null +++ b/src/bluetooth/bluetooth_device.cc @@ -0,0 +1,144 @@ +/* + * 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_device.h" + +#include "common/converter.h" +#include "common/logger.h" + +#include "bluetooth_adapter.h" +#include "bluetooth_class.h" +#include "bluetooth_privilege.h" +#include "bluetooth_util.h" + +namespace extension { +namespace bluetooth { + +using namespace common; + +namespace { +//device +const std::string kDeviceName = "name"; +const std::string kDeviceAddress = "address"; +const std::string kDeviceClass = "deviceClass"; +const std::string kDeviceClassMajor = "major"; +const std::string kDeviceClassMinor = "minor"; +const std::string kDeviceClassService = "services"; +const std::string kDeviceUuids = "uuids"; +const std::string kDeviceIsBonded = "isBonded"; +const std::string kDeviceIsTrusted = "isTrusted"; +const std::string kDeviceIsConnected = "isConnected"; +} + +static void ToJsonFromBTClass(bt_class_s bluetooth_class, picojson::object* device) { + LoggerD("Entered"); + + picojson::object& bt = device->insert(std::make_pair(kDeviceClass, picojson::value(picojson::object()))) + .first->second.get(); + + bt.insert(std::make_pair(kDeviceClassMajor, picojson::value(static_cast( + BluetoothClass::GetMajorValue(bluetooth_class.major_device_class))))); + bt.insert(std::make_pair(kDeviceClassMinor, picojson::value(static_cast( + BluetoothClass::GetMinorValue(bluetooth_class.minor_device_class))))); + + picojson::array& array = bt.insert(std::make_pair(kDeviceClassService, + picojson::value(picojson::array()))).first->second.get(); + + std::vector services_vector = BluetoothClass::getServiceValues( + bluetooth_class.major_service_class_mask); + + for (auto v : services_vector) { + array.push_back(picojson::value(static_cast(v))); + } +} + +static void ToJsonFromUUID(char **service_uuid, int service_count, picojson::object* device) { + LoggerD("Entered"); + + picojson::array& array = device->insert(std::make_pair(kDeviceUuids, picojson::value(picojson::array()))) + .first->second.get(); + + for (int i = 0; i < service_count; i++) { + array.push_back(picojson::value(service_uuid[i])); + } +} + +void BluetoothDevice::ToJson(bt_device_info_s* info, picojson::object* device) { + LoggerD("Entered"); + device->insert(std::make_pair(kDeviceName, picojson::value(std::string(info->remote_name)))); + device->insert(std::make_pair(kDeviceAddress, picojson::value(std::string(info->remote_address)))); + + ToJsonFromBTClass(info->bt_class, device); + ToJsonFromUUID(info->service_uuid, info->service_count, device); +} + +void BluetoothDevice::ToJson(bt_adapter_device_discovery_info_s *info, picojson::object* device) { + LoggerD("Entered"); + + device->insert(std::make_pair(kDeviceName, picojson::value(info->remote_name))); + device->insert(std::make_pair(kDeviceAddress, picojson::value(info->remote_address))); + + ToJsonFromBTClass(info->bt_class, device); + ToJsonFromUUID(info->service_uuid, info->service_count, device); +} + +void BluetoothDevice::ConnectToServiceByUUID(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothSpp); + + const auto& args = util::GetArguments(data); + + BluetoothAdapter::GetInstance().ConnectToServiceByUUID( + FromJson(args, "address"), + FromJson(args, "uuid"), + util::GetAsyncCallbackHandle(data)); + + util::ReportSuccess(out); +} + +void BluetoothDevice::GetBoolValue(const picojson::value& data, picojson::object& out) +{ + LoggerD("Entered"); + + const auto& args = util::GetArguments(data); + const auto& address = FromJson(args, "address"); + const auto& field = FromJson(args, "field"); + + bool value = false; + bt_device_info_s *info = nullptr; + if (bt_adapter_get_bonded_device_info(address.c_str(), &info) == BT_ERROR_NONE && + info != nullptr) { + if (kDeviceIsBonded == field) { + value = info->is_bonded; + } else if (kDeviceIsTrusted == field) { + value = info->is_authorized; + } else if (kDeviceIsConnected == field) { + value = info->is_connected; + } else { + bt_adapter_free_device_info(info); + throw UnknownException("Wrong field passed."); + } + bt_adapter_free_device_info(info); + } else { + throw UnknownException("Unknown error"); + } + + util::ReportSuccess(picojson::value(value), out); +} + +} // namespace bluetooth +} // namespace extension diff --git a/src/bluetooth/bluetooth_device.h b/src/bluetooth/bluetooth_device.h new file mode 100644 index 00000000..30ae7ca1 --- /dev/null +++ b/src/bluetooth/bluetooth_device.h @@ -0,0 +1,68 @@ +/* + * 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_DEVICE_H_ +#define BLUETOOTH_BLUETOOTH_DEVICE_H_ + +#include + +#include "common/picojson.h" + +namespace extension { +namespace bluetooth { + +class BluetoothDevice { +public: + /** + * Signature: @code void connectToServiceByUUID(uuid, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothDevice_connectToServiceByUUID', args: {uuid: uuid}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {socket}} + * @endcode + */ + void ConnectToServiceByUUID(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code BluetoothDevice.is(Bonded, Trusted, Connected); @endcode + * JSON: @code data: {method: 'BluetoothDevice_GetBoolValue', args: {address: address, + * field: field}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: name} + * @endcode + */ + void GetBoolValue(const picojson::value& data, picojson::object& out); + + static void ToJson(bt_device_info_s* info, + picojson::object* device); + static void ToJson(bt_adapter_device_discovery_info_s *info, + picojson::object* device); +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_DEVICE_H_ diff --git a/src/bluetooth/bluetooth_extension.cc b/src/bluetooth/bluetooth_extension.cc new file mode 100644 index 00000000..62bf5951 --- /dev/null +++ b/src/bluetooth/bluetooth_extension.cc @@ -0,0 +1,31 @@ +// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "bluetooth_extension.h" +#include "bluetooth_instance.h" + +namespace { + const char* kBluetooth = "tizen.bluetooth"; +} +// This will be generated from bluetooth_api.js. +extern const char kSource_bluetooth_api[]; + +common::Extension* CreateExtension() { + return new BluetoothExtension; +} + +BluetoothExtension::BluetoothExtension() { + SetExtensionName(kBluetooth); + SetJavaScriptAPI(kSource_bluetooth_api); + const char* entry_points[] = { + NULL + }; + SetExtraJSEntryPoints(entry_points); +} + +BluetoothExtension::~BluetoothExtension() {} + +common::Instance* BluetoothExtension::CreateInstance() { + return &extension::bluetooth::BluetoothInstance::GetInstance(); +} diff --git a/src/bluetooth/bluetooth_extension.h b/src/bluetooth/bluetooth_extension.h new file mode 100644 index 00000000..9f262d58 --- /dev/null +++ b/src/bluetooth/bluetooth_extension.h @@ -0,0 +1,20 @@ +// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BLUETOOTH_BLUETOOTH_EXTENSION_H_ +#define BLUETOOTH_BLUETOOTH_EXTENSION_H_ + +#include "common/extension.h" + +class BluetoothExtension : public common::Extension { + public: + BluetoothExtension(); + virtual ~BluetoothExtension(); + + private: + virtual common::Instance* CreateInstance(); +}; + +#endif // BLUETOOTH_BLUETOOTH_EXTENSION_H_ + diff --git a/src/bluetooth/bluetooth_health_application.cc b/src/bluetooth/bluetooth_health_application.cc new file mode 100644 index 00000000..811d3463 --- /dev/null +++ b/src/bluetooth/bluetooth_health_application.cc @@ -0,0 +1,61 @@ +/* + * 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_health_application.h" + +#include "common/converter.h" +#include "common/logger.h" + +#include "bluetooth_health_profile_handler.h" +#include "bluetooth_privilege.h" +#include "bluetooth_util.h" + +namespace extension { +namespace bluetooth { + +namespace { +const std::string kDataType = "dataType"; +const std::string kName = "name"; +const std::string kId = "_id"; +} // namespace + +using namespace common; + +void BluetoothHealthApplication::Unregister(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothHealth); + + const auto& args = util::GetArguments(data); + + BluetoothHealthProfileHandler::GetInstance().UnregisterSinkAppAsync( + FromJson(args, "id"), + util::GetAsyncCallbackHandle(data)); + + util::ReportSuccess(out); +} + +void BluetoothHealthApplication::ToJson(short data_type, + const std::string& name, + const char* id, + picojson::object* out) { + out->insert(std::make_pair(kDataType, picojson::value(static_cast(data_type)))); + out->insert(std::make_pair(kName, picojson::value(name))); + out->insert(std::make_pair(kId, picojson::value(id))); +} + +} // namespace bluetooth +} // namespace extension diff --git a/src/bluetooth/bluetooth_health_application.h b/src/bluetooth/bluetooth_health_application.h new file mode 100644 index 00000000..d0d28e00 --- /dev/null +++ b/src/bluetooth/bluetooth_health_application.h @@ -0,0 +1,54 @@ +/* + * 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_HEALTH_APPLICATION_H_ +#define BLUETOOTH_BLUETOOTH_HEALTH_APPLICATION_H_ + + +#include "common/picojson.h" + +namespace extension { +namespace bluetooth { + +class BluetoothHealthApplication { +public: + /** + * Signature: @code void unregister(successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothHealthApplication_unregister', args: {}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void Unregister(const picojson::value& data, picojson::object& out); + + static void ToJson(short data_type, + const std::string& name, + const char* id, + picojson::object* out); +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_HEALTH_APPLICATION_H_ diff --git a/src/bluetooth/bluetooth_health_channel.cc b/src/bluetooth/bluetooth_health_channel.cc new file mode 100644 index 00000000..0923a0f1 --- /dev/null +++ b/src/bluetooth/bluetooth_health_channel.cc @@ -0,0 +1,121 @@ +/* + * 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_health_channel.h" + +#include + +#include "common/converter.h" +#include "common/logger.h" +#include "common/platform_exception.h" + +#include "bluetooth_device.h" +#include "bluetooth_privilege.h" +#include "bluetooth_util.h" + +namespace extension { +namespace bluetooth { + +using namespace common; + +namespace { +const std::string kPeer = "peer"; +const std::string kChannelType = "channelType"; +const std::string kApplication = "appId"; +const std::string kIsConnected = "isConnected"; +const std::string kId = "_id"; +} // namespace + +void BluetoothHealthChannel::Close(const picojson::value& data , picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothHealth); + + const auto& args = util::GetArguments(data); + + unsigned int channel = common::stol(FromJson(args, "channel")); + const auto& address = FromJson(args, "address"); + + if (BT_ERROR_NONE != bt_hdp_disconnect(address.c_str(), channel)) { + throw UnknownException("Unknown error"); + } + + util::ReportSuccess(out); +} + +void BluetoothHealthChannel::SendData(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothHealth); + + const auto& args = util::GetArguments(data); + + unsigned int channel = common::stol(FromJson(args, "channel")); + const auto& binary_data = FromJson(args, "data"); + const auto data_size = binary_data.size(); + + std::unique_ptr data_ptr{new char[data_size]}; + + for (std::size_t i = 0; i < data_size; ++i) { + data_ptr[i] = static_cast(binary_data[i].get()); + } + + if (BT_ERROR_NONE != bt_hdp_send_data(channel, data_ptr.get(), data_size)) { + LoggerE("bt_hdp_send_data() failed"); + throw UnknownException("Unknown error"); + } + + util::ReportSuccess(picojson::value(static_cast(data_size)), out); +} + +void BluetoothHealthChannel::ToJson(unsigned int channel, + bt_hdp_channel_type_e type, + picojson::object* out) { + const char* type_str = "UNKNOWN"; + + switch (type) { + case BT_HDP_CHANNEL_TYPE_RELIABLE: + type_str = "RELIABLE"; + break; + + case BT_HDP_CHANNEL_TYPE_STREAMING: + type_str = "STREAMING"; + break; + + default: + LoggerE("Unknown HDP channel type: %d", type); + break; + } + + out->insert(std::make_pair(kId, picojson::value(std::to_string(channel)))); + out->insert(std::make_pair(kChannelType, picojson::value(type_str))); + out->insert(std::make_pair(kIsConnected, picojson::value(true))); +} + +void BluetoothHealthChannel::ToJson(unsigned int channel, + bt_hdp_channel_type_e type, + bt_device_info_s* device_info, + const char* app_id, + picojson::object* out) { + ToJson(channel, type, out); + auto& device = out->insert(std::make_pair(kPeer, picojson::value(picojson::object()))) + .first->second.get(); + BluetoothDevice::ToJson(device_info, &device); + out->insert(std::make_pair(kApplication, picojson::value(app_id))); +} + +} // namespace bluetooth +} // namespace extension diff --git a/src/bluetooth/bluetooth_health_channel.h b/src/bluetooth/bluetooth_health_channel.h new file mode 100644 index 00000000..482a71f3 --- /dev/null +++ b/src/bluetooth/bluetooth_health_channel.h @@ -0,0 +1,66 @@ +/* + * 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_HEALTH_CHANNEL_H_ +#define BLUETOOTH_BLUETOOTH_HEALTH_CHANNEL_H_ + +#include + +#include "common/picojson.h" + +namespace extension { +namespace bluetooth { + +class BluetoothHealthChannel { +public: + /** + * Signature: @code void close(); @endcode + * JSON: @code data: {method: 'BluetoothHealthChannel_close', args: {}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void Close(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code unsigned long sendData(data[]); @endcode + * JSON: @code data: {method: 'BluetoothHealthChannel_sendData', args: {data: data}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {bytes_sent}} + */ + void SendData(const picojson::value& data, picojson::object& out); + + static void ToJson(unsigned int channel, + bt_hdp_channel_type_e type, + picojson::object* out); + + static void ToJson(unsigned int channel, + bt_hdp_channel_type_e type, + bt_device_info_s* device_info, + const char* app_id, + picojson::object* out); +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_HEALTH_CHANNEL_H_ diff --git a/src/bluetooth/bluetooth_health_profile_handler.cc b/src/bluetooth/bluetooth_health_profile_handler.cc new file mode 100644 index 00000000..d19cec25 --- /dev/null +++ b/src/bluetooth/bluetooth_health_profile_handler.cc @@ -0,0 +1,388 @@ +/* + * 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_health_profile_handler.h" + +#include "common/converter.h" +#include "common/logger.h" +#include "common/platform_exception.h" +#include "common/task-queue.h" + +#include "bluetooth_adapter.h" +#include "bluetooth_health_application.h" +#include "bluetooth_health_channel.h" +#include "bluetooth_privilege.h" +#include "bluetooth_util.h" + +namespace extension { +namespace bluetooth { + +using namespace common; + +namespace { +const std::string kId = "id"; +const std::string kEvent = "event"; +const std::string kData = "data"; +const std::string kOnConnect = "onconnect"; +const std::string kOnClose = "onclose"; +const std::string kOnMessage = "onmessage"; +const std::string kChangeCallback = "BluetoothHealthChannelChangeCallback"; +} //namespace + +BluetoothHealthProfileHandler& BluetoothHealthProfileHandler::GetInstance() { + static BluetoothHealthProfileHandler instance; + return instance; +} + +BluetoothHealthProfileHandler::BluetoothHealthProfileHandler() { + // initialize listeners + if (BT_ERROR_NONE != bt_hdp_set_connection_state_changed_cb(OnConnected, + OnDisconnected, + this)) { + LoggerE("bt_hdp_set_connection_state_changed_cb() failed"); + } + + if (BT_ERROR_NONE != bt_hdp_set_data_received_cb(OnDataReceived, this)) { + LoggerE("bt_hdp_set_data_received_cb() failed"); + } +} + +BluetoothHealthProfileHandler::~BluetoothHealthProfileHandler() { + bt_hdp_unset_connection_state_changed_cb(); + bt_hdp_unset_data_received_cb(); + + for (auto& it : registered_health_apps_) { + bt_hdp_unregister_sink_app(it.c_str()); + } +} + +void BluetoothHealthProfileHandler::OnConnected(int result, + const char* remote_address, + const char* app_id, + bt_hdp_channel_type_e type, + unsigned int channel, + void* user_data) { + LoggerD("Entered"); + + BluetoothHealthProfileHandler* object = static_cast(user_data); + + if (!object) { + LoggerW("user_data is NULL"); + return; + } + + if (BT_ERROR_NONE != result) { + LoggerD("Not BT_ERROR_NONE: %d", result); + } + + LoggerD("Connected app: %s", app_id); + LoggerD("Connected channel: %u", channel); + + const auto iter = object->registered_health_apps_.find(app_id); + + if (iter == object->registered_health_apps_.end()) { + LoggerW("This app is not registered: %s", app_id); + return; + } + + bool channel_added = false; + + if (BT_ERROR_NONE == result) { + // send BluetoothHealthApplication.onconnect notification + bt_device_info_s* device_info = nullptr; + + if (BT_ERROR_NONE == bt_adapter_get_bonded_device_info(remote_address, &device_info) && + nullptr != device_info) { + LoggerD("invoke BluetoothHealthApplication.onconnect"); + + object->connected_channels_.insert(channel); + channel_added = true; + + picojson::value response{picojson::object()}; + picojson::object& response_obj = response.get(); + response_obj.insert(std::make_pair(kId, picojson::value(app_id))); + response_obj.insert(std::make_pair(kEvent, picojson::value(kOnConnect))); + + picojson::value result = picojson::value(picojson::object()); + + BluetoothHealthChannel::ToJson(channel, + type, + device_info, + app_id, + &result.get()); + + util::ReportSuccess(result, response_obj); + bt_adapter_free_device_info(device_info); + + util::FireEvent("BLUETOOTH_HEALTH_APPLICATION_CHANGED", response); + } else { + LoggerE("Failed to get device info"); + } + } + + // Handle requests from BluetoothHealthProfileHandler.connectToSource() + const auto request = object->connection_requests_.find(app_id); + + if (request != object->connection_requests_.end()) { + LoggerD("Requested connection"); + + std::shared_ptr response{new picojson::value(picojson::object())}; + + if (BT_ERROR_NONE == result) { + if (!channel_added) { + object->connected_channels_.insert(channel); + } + + picojson::value result = picojson::value(picojson::object()); + + BluetoothHealthChannel::ToJson(channel, + type, + &result.get()); + + util::ReportSuccess(result, response->get()); + } else { + LoggerE("Failed to establish a connection with health profile"); + util::ReportError(UnknownException("Failed to establish a connection with health profile"), + response->get()); + } + + util::AsyncResponse(request->second, response); + + // request was handled, remove + object->connection_requests_.erase(request); + } + else { + LoggerD("This connection was not requested."); + } +} + +void BluetoothHealthProfileHandler::OnDisconnected(int result, + const char* /* remote_address */, + unsigned int channel, + void* user_data) { + LoggerD("Entered"); + + BluetoothHealthProfileHandler* object = static_cast(user_data); + + if (!object) { + LoggerW("user_data is NULL"); + return; + } + + auto it = object->connected_channels_.find(channel); + if (BT_ERROR_NONE == result && object->connected_channels_.end() != it) { + object->connected_channels_.erase(it); + picojson::value value = picojson::value(picojson::object()); + picojson::object* data_obj = &value.get(); + + data_obj->insert(std::make_pair(kEvent, picojson::value(kOnClose))); + data_obj->insert(std::make_pair(kId, picojson::value(std::to_string(channel)))); + util::FireEvent(kChangeCallback, value); + } +} + +void BluetoothHealthProfileHandler::OnDataReceived(unsigned int channel, + const char* data, + unsigned int size, + void* user_data) { + LoggerD("Entered"); + + BluetoothHealthProfileHandler* object = static_cast(user_data); + + if (!object) { + LoggerW("user_data is NULL"); + return; + } + + auto it = object->connected_channels_.find(channel); + if (object->connected_channels_.end() != it) { + picojson::value value = picojson::value(picojson::object()); + picojson::object* data_obj = &value.get(); + + data_obj->insert(std::make_pair(kEvent, picojson::value(kOnMessage))); + data_obj->insert(std::make_pair(kId, picojson::value(std::to_string(channel)))); + + picojson::array& array = data_obj->insert(std::make_pair(kData, picojson::value( + picojson::array()))).first->second.get(); + + for (unsigned int i = 0; i < size; i++) { + array.push_back(picojson::value(static_cast(data[i]))); + } + + util::FireEvent(kChangeCallback, value); + } +} + +void BluetoothHealthProfileHandler::RegisterSinkApp(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothHealth); + + const auto& args = util::GetArguments(data); + const auto data_type = static_cast(FromJson(args, "dataType")); + const auto& name = FromJson(args, "name"); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + + auto register_app = [data_type, name, this](const std::shared_ptr& response) -> void { + LoggerD("Entered"); + + try { + char* app_id = nullptr; + const int ret = bt_hdp_register_sink_app(data_type, &app_id); + + switch (ret) { + case BT_ERROR_NONE: + { + LoggerD("Registered app: %s", app_id); + + this->registered_health_apps_.insert(app_id); + + picojson::value result = picojson::value(picojson::object()); + BluetoothHealthApplication::ToJson(data_type, + name, + app_id, + &result.get()); + util::ReportSuccess(result, response->get()); + } + break; + + case BT_ERROR_NOT_ENABLED: + LoggerE("Bluetooth device is turned off"); + throw ServiceNotAvailableException("Bluetooth device is turned off"); + break; + + default: + LoggerE("bt_hdp_register_sink_app() failed: %d", ret); + throw UnknownException("Unknown error"); + break; + } + } catch (const PlatformException& e) { + util::ReportError(e, response->get()); + } + }; + + auto register_app_response = [callback_handle](const std::shared_ptr& response) -> void { + util::SyncResponse(callback_handle, response); + }; + + TaskQueue::GetInstance().Queue(register_app, + register_app_response, + std::shared_ptr(new picojson::value(picojson::object()))); + + util::ReportSuccess(out); +} + +void BluetoothHealthProfileHandler::ConnectToSource(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothHealth); + + const auto& args = util::GetArguments(data); + const auto& address = FromJson(args, "address"); + const auto& app_id = FromJson(args, "appId"); + + LoggerD("address: %s", address.c_str()); + LoggerD("app ID: %s", app_id.c_str()); + + const auto callback_handle = util::GetAsyncCallbackHandle(data); + + try { + const int ret = bt_hdp_connect_to_source(address.c_str(), app_id.c_str()); + + switch (ret) { + case BT_ERROR_NONE: { + LoggerD("bt_hdp_connect_to_source() succeeded"); + + connection_requests_.insert(std::make_pair(app_id, callback_handle)); + break; + } + + case BT_ERROR_NOT_ENABLED: + throw ServiceNotAvailableException("Bluetooth device is turned off"); + break; + + case BT_ERROR_INVALID_PARAMETER: + case BT_ERROR_REMOTE_DEVICE_NOT_BONDED: + throw InvalidValuesException("Invalid value"); + break; + + default: + throw UnknownException("Unknown error"); + break; + } + } catch (const PlatformException& e) { + LoggerD("Exception: %s", e.message().c_str()); + std::shared_ptr response{new picojson::value(picojson::object())}; + util::ReportError(e, response->get()); + util::AsyncResponse(callback_handle, response); + } + + util::ReportSuccess(out); +} + +void BluetoothHealthProfileHandler::UnregisterSinkAppAsync(const std::string& app_id, + int callback_handle) { + LoggerD("Entered"); + + auto unregister_app = [app_id, this](const std::shared_ptr& response) -> void { + LoggerD("Entered"); + + auto iter = this->registered_health_apps_.find(app_id); + + if (iter != this->registered_health_apps_.end()) { + LoggerD("Found registered Health Application: %s", app_id.c_str()); + + try { + const int ret = bt_hdp_unregister_sink_app(app_id.c_str()); + + switch(ret) { + case BT_ERROR_NONE: + this->registered_health_apps_.erase(iter); + break; + + case BT_ERROR_NOT_ENABLED: + LoggerE("Bluetooth device is turned off"); + throw ServiceNotAvailableException("Bluetooth device is turned off"); + break; + + default: + LoggerE("bt_hdp_unregister_sink_app() failed: %d", ret); + throw UnknownException("Unknown error"); + break; + } + } catch (const PlatformException& e) { + util::ReportError(e, response->get()); + return; + } + } else { + LoggerD("Already unregistered"); + } + + util::ReportSuccess(response->get()); + }; + + auto unregister_app_response = [callback_handle](const std::shared_ptr& response) -> void { + util::SyncResponse(callback_handle, response); + }; + + TaskQueue::GetInstance().Queue(unregister_app, + unregister_app_response, + std::shared_ptr(new picojson::value(picojson::object()))); +} + +} // namespace bluetooth +} // namespace extension diff --git a/src/bluetooth/bluetooth_health_profile_handler.h b/src/bluetooth/bluetooth_health_profile_handler.h new file mode 100644 index 00000000..672e111c --- /dev/null +++ b/src/bluetooth/bluetooth_health_profile_handler.h @@ -0,0 +1,103 @@ +/* + * 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_HEALTH_PROFILE_HANDLER_H_ +#define BLUETOOTH_BLUETOOTH_HEALTH_PROFILE_HANDLER_H_ + +#include + +#include + +#include "common/picojson.h" + +namespace extension { +namespace bluetooth { + +class BluetoothHealthProfileHandler { +public: + /** + * Signature: @code void registerSinkApp(dataType, name, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothHealthProfileHandler_registerSinkApp', + * args: {dataType: dataType, name: name}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {application}} + * @endcode + */ + void RegisterSinkApp(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void connectToSource(peer, application, successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothHealthProfileHandler_connectToSource', + * args: {peer: peer, application: application}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {channel}} + * @endcode + */ + void ConnectToSource(const picojson::value& data, picojson::object& out); + + static BluetoothHealthProfileHandler& GetInstance(); + + ~BluetoothHealthProfileHandler(); + + void UnregisterSinkAppAsync(const std::string& app_id, int callback_handle); + +private: + BluetoothHealthProfileHandler(); + BluetoothHealthProfileHandler(const BluetoothHealthProfileHandler&) = delete; + BluetoothHealthProfileHandler& operator=(const BluetoothHealthProfileHandler&) = delete; + + static void OnConnected(int result, + const char* remote_address, + const char* app_id, + bt_hdp_channel_type_e type, + unsigned int channel, + void* user_data); + + static void OnDisconnected(int result, + const char* remote_address, + unsigned int channel, + void* user_data); + + static void OnDataReceived(unsigned int channel, + const char* data, + unsigned int size, + void* user_data); + + std::set registered_health_apps_; + std::map connection_requests_; + std::set connected_channels_; +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_HEALTH_PROFILE_HANDLER_H_ diff --git a/src/bluetooth/bluetooth_instance.cc b/src/bluetooth/bluetooth_instance.cc new file mode 100644 index 00000000..439c06d9 --- /dev/null +++ b/src/bluetooth/bluetooth_instance.cc @@ -0,0 +1,136 @@ + +// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "bluetooth_instance.h" + +#include "common/converter.h" +#include "common/logger.h" + +#include "bluetooth_adapter.h" +#include "bluetooth_device.h" +#include "bluetooth_health_application.h" +#include "bluetooth_health_channel.h" +#include "bluetooth_health_profile_handler.h" +#include "bluetooth_service_handler.h" +#include "bluetooth_socket.h" +#include "bluetooth_util.h" + +namespace extension { +namespace bluetooth { + +using namespace common; + +namespace { + +BluetoothAdapter* bluetooth_adapter = &BluetoothAdapter::GetInstance(); +BluetoothDevice bluetooth_device; +BluetoothHealthApplication bluetooth_health_application; +BluetoothHealthChannel bluetooth_health_channel; +BluetoothHealthProfileHandler* bluetooth_health_profile_handler = &BluetoothHealthProfileHandler::GetInstance(); +BluetoothServiceHandler bluetooth_service_handler; +BluetoothSocket bluetooth_socket; + +void CheckPrivilege(const picojson::value& data, picojson::object& out) +{ + const auto& args = util::GetArguments(data); + const auto& privilege = FromJson(args, "privilege"); + util::CheckAccess(privilege); + + util::ReportSuccess(out); +} + +} // namespace + +BluetoothInstance& BluetoothInstance::GetInstance() +{ + static BluetoothInstance instance; + return instance; +} + +BluetoothInstance::BluetoothInstance() +{ + LoggerD("Entered"); + using namespace std::placeholders; + + // BluetoothAdapter + RegisterHandler("BluetoothAdapter_setName", + std::bind(&BluetoothAdapter::SetName, bluetooth_adapter, _1, _2)); + RegisterHandler("BluetoothAdapter_setPowered", + std::bind(&BluetoothAdapter::SetPowered, bluetooth_adapter, _1, _2)); + RegisterHandler("BluetoothAdapter_setVisible", + std::bind(&BluetoothAdapter::SetVisible, bluetooth_adapter, _1, _2)); + RegisterSyncHandler("BluetoothAdapter_discoverDevices", + std::bind(&BluetoothAdapter::DiscoverDevices, bluetooth_adapter, _1, _2)); + RegisterHandler("BluetoothAdapter_stopDiscovery", + std::bind(&BluetoothAdapter::StopDiscovery, bluetooth_adapter, _1, _2)); + RegisterHandler("BluetoothAdapter_getKnownDevices", + std::bind(&BluetoothAdapter::GetKnownDevices, bluetooth_adapter, _1, _2)); + RegisterHandler("BluetoothAdapter_getDevice", + std::bind(&BluetoothAdapter::GetDevice, bluetooth_adapter, _1, _2)); + RegisterHandler("BluetoothAdapter_createBonding", + std::bind(&BluetoothAdapter::CreateBonding, bluetooth_adapter, _1, _2)); + RegisterHandler("BluetoothAdapter_destroyBonding", + std::bind(&BluetoothAdapter::DestroyBonding, bluetooth_adapter, _1, _2)); + RegisterHandler("BluetoothAdapter_registerRFCOMMServiceByUUID", + std::bind(&BluetoothAdapter::RegisterRFCOMMServiceByUUID, bluetooth_adapter, _1, _2)); + RegisterSyncHandler("BluetoothAdapter_getBluetoothProfileHandler", + std::bind(&BluetoothAdapter::GetBluetoothProfileHandler, bluetooth_adapter, _1, _2)); + RegisterSyncHandler("BluetoothAdapter_getName", + std::bind(&BluetoothAdapter::GetName, bluetooth_adapter, _1, _2)); + RegisterSyncHandler("BluetoothAdapter_getAddress", + std::bind(&BluetoothAdapter::GetAddress, bluetooth_adapter, _1, _2)); + RegisterSyncHandler("BluetoothAdapter_getPowered", + std::bind(&BluetoothAdapter::GetPowered, bluetooth_adapter, _1, _2)); + RegisterSyncHandler("BluetoothAdapter_getVisible", + std::bind(&BluetoothAdapter::GetVisible, bluetooth_adapter, _1, _2)); + RegisterSyncHandler("BluetoothAdapter_isServiceConnected", + std::bind(&BluetoothAdapter::IsServiceConnected, bluetooth_adapter, _1, _2)); + + // BluetoothDevice + RegisterHandler("BluetoothDevice_connectToServiceByUUID", + std::bind(&BluetoothDevice::ConnectToServiceByUUID, &bluetooth_device, _1, _2)); + RegisterSyncHandler("BluetoothDevice_getBoolValue", + std::bind(&BluetoothDevice::GetBoolValue, &bluetooth_device, _1, _2)); + + // BluetoothHealthApplication + RegisterHandler("BluetoothHealthApplication_unregister", + std::bind(&BluetoothHealthApplication::Unregister, &bluetooth_health_application, _1, _2)); + + // BluetoothHealthChannel + RegisterSyncHandler("BluetoothHealthChannel_close", + std::bind(&BluetoothHealthChannel::Close, &bluetooth_health_channel, _1, _2)); + RegisterSyncHandler("BluetoothHealthChannel_sendData", + std::bind(&BluetoothHealthChannel::SendData, &bluetooth_health_channel, _1, _2)); + + // BluetoothHealthProfileHandler + RegisterHandler("BluetoothHealthProfileHandler_registerSinkApp", + std::bind(&BluetoothHealthProfileHandler::RegisterSinkApp, bluetooth_health_profile_handler, _1, _2)); + RegisterHandler("BluetoothHealthProfileHandler_connectToSource", + std::bind(&BluetoothHealthProfileHandler::ConnectToSource, bluetooth_health_profile_handler, _1, _2)); + + // BluetoothServiceHandler + RegisterHandler("BluetoothServiceHandler_unregister", + std::bind(&BluetoothServiceHandler::Unregister, &bluetooth_service_handler, _1, _2)); + + // BluetoothSocket + RegisterSyncHandler("BluetoothSocket_writeData", + std::bind(&BluetoothSocket::WriteData, &bluetooth_socket, _1, _2)); + RegisterSyncHandler("BluetoothSocket_readData", + std::bind(&BluetoothSocket::ReadData, &bluetooth_socket, _1, _2)); + RegisterSyncHandler("BluetoothSocket_close", + std::bind(&BluetoothSocket::Close, &bluetooth_socket, _1, _2)); + + // other + RegisterSyncHandler("Bluetooth_checkPrivilege", CheckPrivilege); +} + +BluetoothInstance::~BluetoothInstance() +{ + LoggerD("Entered"); +} + +} // namespace bluetooth +} // namespace extension + diff --git a/src/bluetooth/bluetooth_instance.h b/src/bluetooth/bluetooth_instance.h new file mode 100644 index 00000000..22aded5b --- /dev/null +++ b/src/bluetooth/bluetooth_instance.h @@ -0,0 +1,24 @@ +// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BLUETOOTH_BLUETOOTH_INSTANCE_H_ +#define BLUETOOTH_BLUETOOTH_INSTANCE_H_ + +#include "common/extension.h" + +namespace extension { +namespace bluetooth { + +class BluetoothInstance: public common::ParsedInstance { +public: + static BluetoothInstance& GetInstance(); +private: + BluetoothInstance(); + virtual ~BluetoothInstance(); +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_INSTANCE_H_ diff --git a/src/bluetooth/bluetooth_privilege.h b/src/bluetooth/bluetooth_privilege.h new file mode 100644 index 00000000..9c81b1ce --- /dev/null +++ b/src/bluetooth/bluetooth_privilege.h @@ -0,0 +1,36 @@ +/* + * 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_PRIVILEGE_H_ +#define BLUETOOTH_BLUETOOTH_PRIVILEGE_H_ + +#include + +namespace extension { +namespace bluetooth { + +namespace Privilege { +const std::string kBluetoothAdmin = "http://tizen.org/privilege/bluetooth.admin"; +const std::string kBluetoothManager = "http://tizen.org/privilege/bluetoothmanager"; +const std::string kBluetoothGap = "http://tizen.org/privilege/bluetooth.gap"; +const std::string kBluetoothSpp = "http://tizen.org/privilege/bluetooth.spp"; +const std::string kBluetoothHealth = "http://tizen.org/privilege/bluetooth.health"; +} // namespace Privilege + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_PRIVILEGE_H_ diff --git a/src/bluetooth/bluetooth_service_handler.cc b/src/bluetooth/bluetooth_service_handler.cc new file mode 100644 index 00000000..02deb1e0 --- /dev/null +++ b/src/bluetooth/bluetooth_service_handler.cc @@ -0,0 +1,46 @@ +/* + * 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_service_handler.h" + +#include "common/converter.h" +#include "common/logger.h" + +#include "bluetooth_adapter.h" +#include "bluetooth_privilege.h" +#include "bluetooth_util.h" + +namespace extension { +namespace bluetooth { + +using namespace common; + +void BluetoothServiceHandler::Unregister(const picojson::value& data, picojson::object& out) { + LoggerD("Entered"); + + util::CheckAccess(Privilege::kBluetoothSpp); + + const auto& args = util::GetArguments(data); + + BluetoothAdapter::GetInstance().UnregisterUUID( + FromJson(args, "uuid"), + util::GetAsyncCallbackHandle(data)); + + util::ReportSuccess(out); +} + +} // namespace bluetooth +} // namespace extension diff --git a/src/bluetooth/bluetooth_service_handler.h b/src/bluetooth/bluetooth_service_handler.h new file mode 100644 index 00000000..80da12f3 --- /dev/null +++ b/src/bluetooth/bluetooth_service_handler.h @@ -0,0 +1,48 @@ +/* + * 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_SERVICE_HANDLER_H_ +#define BLUETOOTH_BLUETOOTH_SERVICE_HANDLER_H_ + +#include "common/picojson.h" + +namespace extension { +namespace bluetooth { + +class BluetoothServiceHandler { +public: + /** + * Signature: @code void unregister(successCallback, errorCallback); @endcode + * JSON: @code data: {method: 'BluetoothServiceHandler_unregister', args: {}} @endcode + * Invocation: @code native.call(request, result_callback); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + * Result callback: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void Unregister(const picojson::value& data, picojson::object& out); +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_SERVICE_HANDLER_H_ diff --git a/src/bluetooth/bluetooth_socket.cc b/src/bluetooth/bluetooth_socket.cc new file mode 100644 index 00000000..7925145f --- /dev/null +++ b/src/bluetooth/bluetooth_socket.cc @@ -0,0 +1,137 @@ +/* + * 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_socket.h" + +#include + +#include "common/converter.h" +#include "common/logger.h" + +#include "bluetooth_adapter.h" +#include "bluetooth_device.h" +#include "bluetooth_privilege.h" +#include "bluetooth_util.h" + +namespace extension { +namespace bluetooth { + +namespace { +const std::string kBluetoothSocketId = "id"; +const std::string kBluetoothSocketUuid = "uuid"; +const std::string kBluetoothSocketState = "state"; +const std::string kBluetoothSocketPeer = "peer"; +const std::string kBluetoothSocketStateOpen = "OPEN"; +// error +const int kBluetoothError = -1; +} + +using namespace common; + +void BluetoothSocket::WriteData(const picojson::value& data, picojson::object& out) { + LoggerD("Enter"); + + util::CheckAccess(Privilege::kBluetoothSpp); + + const auto& args = util::GetArguments(data); + + int socket = common::stol(FromJson(args, "id")); + const auto& binary_data = FromJson(args, "data"); + const auto data_size = binary_data.size(); + + std::unique_ptr data_ptr{new char[data_size]}; + + for (std::size_t i = 0; i < data_size; ++i) { + data_ptr[i] = static_cast(binary_data[i].get()); + } + + if (kBluetoothError == bt_socket_send_data(socket, data_ptr.get(), data_size)) { + LoggerE("bt_socket_send_data() failed"); + throw UnknownException("Unknown error"); + } + + util::ReportSuccess(picojson::value(static_cast(data_size)), out); +} + +void BluetoothSocket::ReadData(const picojson::value& data, picojson::object& out) { + LoggerD("Enter"); + + util::CheckAccess(Privilege::kBluetoothSpp); + + const auto& args = util::GetArguments(data); + + int socket = common::stol(FromJson(args, "id")); + + auto binary_data = BluetoothAdapter::GetInstance().ReadSocketData(socket); + picojson::value ret = picojson::value(picojson::array()); + picojson::array& array = ret.get(); + + for (auto val : binary_data) { + array.push_back(picojson::value(static_cast(val))); + } + + BluetoothAdapter::GetInstance().ClearSocketData(socket); + + util::ReportSuccess(ret, out); +} + +void BluetoothSocket::Close(const picojson::value& data, picojson::object& out) { + LoggerD("Enter"); + + util::CheckAccess(Privilege::kBluetoothSpp); + + const auto& args = util::GetArguments(data); + + int socket = common::stol(FromJson(args, "id")); + + if (BT_ERROR_NONE != bt_socket_disconnect_rfcomm(socket)) { + LoggerE("bt_socket_disconnect_rfcomm() failed"); + throw UnknownException("Unknown error"); + } + + util::ReportSuccess(out); +} + +picojson::value BluetoothSocket::ToJson(bt_socket_connection_s* connection) { + LoggerD("Enter"); + + picojson::value ret = picojson::value(picojson::object()); + auto& ret_obj = ret.get(); + + ret_obj.insert(std::make_pair(kBluetoothSocketId, + picojson::value(std::to_string(connection->socket_fd)))); + ret_obj.insert(std::make_pair(kBluetoothSocketUuid, + picojson::value(connection->service_uuid))); + ret_obj.insert(std::make_pair(kBluetoothSocketState, + picojson::value(kBluetoothSocketStateOpen))); + + bt_device_info_s* device_info = nullptr; + + if (BT_ERROR_NONE == bt_adapter_get_bonded_device_info(connection->remote_address, &device_info) && + nullptr != device_info) { + picojson::value& device = ret_obj.insert(std::make_pair(kBluetoothSocketPeer, + picojson::value(picojson::object()))).first->second; + BluetoothDevice::ToJson(device_info, &device.get()); + bt_adapter_free_device_info(device_info); + } else { + LoggerE("Peer not found"); + } + + return ret; +} + +} // namespace bluetooth +} // namespace extension diff --git a/src/bluetooth/bluetooth_socket.h b/src/bluetooth/bluetooth_socket.h new file mode 100644 index 00000000..e8b0396f --- /dev/null +++ b/src/bluetooth/bluetooth_socket.h @@ -0,0 +1,71 @@ +/* + * 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_SOCKET_H__ +#define BLUETOOTH_BLUETOOTH_SOCKET_H__ + +#include + +#include "common/picojson.h" + +namespace extension { +namespace bluetooth { + +class BluetoothSocket { +public: + /** + * Signature: @code unsigned long writeData(data[]); @endcode + * JSON: @code data: {method: 'BluetoothSocket_writeData', args: {data: data}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {bytes_sent}} + * @endcode + */ + void WriteData(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code byte[] readData(); @endcode + * JSON: @code data: {method: 'BluetoothSocket_readData', args: {}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success', result: {bytes_read}} + * @endcode + */ + void ReadData(const picojson::value& data, picojson::object& out); + + /** + * Signature: @code void close(); @endcode + * JSON: @code data: {method: 'BluetoothSocket_close', args: {}} @endcode + * Invocation: @code native.callSync(request); @endcode + * Return: + * @code + * {status: 'error', error: {name, message}} + * {status: 'success'} + * @endcode + */ + void Close(const picojson::value& data, picojson::object& out); + + static picojson::value ToJson(bt_socket_connection_s* connection); +}; + +} // namespace bluetooth +} // namespace extension + +#endif // BLUETOOTH_BLUETOOTH_SOCKET_H__ diff --git a/src/bluetooth/bluetooth_util.cc b/src/bluetooth/bluetooth_util.cc new file mode 100644 index 00000000..670e993e --- /dev/null +++ b/src/bluetooth/bluetooth_util.cc @@ -0,0 +1,86 @@ +// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved. +// 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 "common/logger.h" +#include "common/task-queue.h" + +#include "bluetooth_instance.h" + +namespace extension { +namespace bluetooth { +namespace util { + +namespace { +const char* JSON_CALLBACK_ID = "callbackId"; +const char* JSON_LISTENER_ID = "listenerId"; +const char* JSON_STATUS = "status"; +const char* JSON_RESULT = "result"; +const char* JSON_CALLBACK_SUCCCESS = "success"; +const char* JSON_CALLBACK_ERROR = "error"; +const char* JSON_DATA = "args"; +} // namespace + + +void CheckAccess(const std::string& privilege) { + // TODO: check access to privilege, throw exception on failure +} + +void AsyncResponse(double callback_handle, const std::shared_ptr& response) { + common::TaskQueue::GetInstance().Async([callback_handle](const std::shared_ptr& response) { + SyncResponse(callback_handle, response); + }, response); +} + +void SyncResponse(double callback_handle, const std::shared_ptr& response) { + auto& obj = response->get(); + obj[JSON_CALLBACK_ID] = picojson::value(callback_handle); + BluetoothInstance::GetInstance().PostMessage(response->serialize().c_str()); +} + +void FireEvent(const std::string& event, picojson::value& value) { + auto& obj = value.get(); + obj[JSON_LISTENER_ID] = picojson::value(event); + BluetoothInstance::GetInstance().PostMessage(value.serialize().c_str()); +} + +void FireEvent(const std::string& event, const picojson::value& value) { + picojson::value v{value}; + FireEvent(event, v); +} + +void FireEvent(const std::string& event, const std::shared_ptr& value) { + FireEvent(event, *value.get()); +} + +void ReportSuccess(picojson::object& out) { + out.insert(std::make_pair(JSON_STATUS, picojson::value(JSON_CALLBACK_SUCCCESS))); +} + +void ReportSuccess(const picojson::value& result, picojson::object& out) { + ReportSuccess(out); + out.insert(std::make_pair(JSON_RESULT, result)); +} + +void ReportError(picojson::object& out) { + out.insert(std::make_pair(JSON_STATUS, picojson::value(JSON_CALLBACK_ERROR))); +} + +void ReportError(const common::PlatformException& ex, picojson::object& out) { + ReportError(out); + out.insert(std::make_pair(JSON_CALLBACK_ERROR, ex.ToJSON())); +} + +double GetAsyncCallbackHandle(const picojson::value& data) { + return data.get(JSON_CALLBACK_ID).get(); +} + +const picojson::object& GetArguments(const picojson::value& data) { + return data.get(); +} + +} // util +} // bluetooth +} // extension diff --git a/src/bluetooth/bluetooth_util.h b/src/bluetooth/bluetooth_util.h new file mode 100644 index 00000000..97cbc74f --- /dev/null +++ b/src/bluetooth/bluetooth_util.h @@ -0,0 +1,41 @@ +// Copyright 2014 Samsung Electronics Co, Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BLUETOOTH_BLUETOOTH_UTIL_H_ +#define BLUETOOTH_BLUETOOTH_UTIL_H_ + +#include +#include + +#include "common/picojson.h" +#include "common/platform_exception.h" + +namespace extension { +namespace bluetooth { +namespace util { + +void CheckAccess(const std::string& privilege); + +void AsyncResponse(double callback_handle, const std::shared_ptr& response); +void SyncResponse(double callback_handle, const std::shared_ptr& response); + +void FireEvent(const std::string& event, picojson::value& value); +void FireEvent(const std::string& event, const picojson::value& value); +void FireEvent(const std::string& event, const std::shared_ptr& value); + +void ReportSuccess(picojson::object& out); +void ReportSuccess(const picojson::value& result, picojson::object& out); + +void ReportError(picojson::object& out); +void ReportError(const common::PlatformException& ex, picojson::object& out); + +double GetAsyncCallbackHandle(const picojson::value& data); + +const picojson::object& GetArguments(const picojson::value& data); + +} // util +} // bluetooth +} // extension + +#endif // BLUETOOTH_BLUETOOTH_UTIL_H_ diff --git a/src/tizen-wrt.gyp b/src/tizen-wrt.gyp index c7aa0752..221e8296 100644 --- a/src/tizen-wrt.gyp +++ b/src/tizen-wrt.gyp @@ -24,6 +24,7 @@ 'extension_host_os == "mobile"', { 'dependencies': [ 'badge/badge.gyp:*', + 'bluetooth/bluetooth.gyp:*', 'callhistory/callhistory.gyp:*', 'contact/contact.gyp:*', 'calendar/calendar.gyp:*',