From: Pawel Andruszkiewicz Date: Mon, 11 May 2015 09:48:25 +0000 (+0200) Subject: [BluetoothLE] Added listeners for characteristic. X-Git-Tag: submit/tizen_tv/20150603.064601~1^2~32 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2a3d3f2ec9e702cee8d15a2a9a6b74f82ddf6889;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [BluetoothLE] Added listeners for characteristic. Change-Id: Ia4ce7dd4e0806dbf8e33776d02bdaf73d88b899d Signed-off-by: Pawel Andruszkiewicz --- diff --git a/src/bluetooth/bluetooth_api.js b/src/bluetooth/bluetooth_api.js index 8376294c..472e193a 100644 --- a/src/bluetooth/bluetooth_api.js +++ b/src/bluetooth/bluetooth_api.js @@ -1702,8 +1702,43 @@ var BluetoothGATTCharacteristic = function(data, address) { throw native.getErrorObject(result); } }; + + BluetoothGATTCharacteristic.prototype.addValueChangeListener = function() { + console.log('Entered BluetoothGATTCharacteristic.addValueChangeListener()'); + + xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH); + + var args = AV.validateMethod(arguments, [{ + name: 'callback', + type: AV.Types.FUNCTION + }]); + + var callArgs = { handle: handle_, address : address_ }; + + var callback = function(event) { + if (event.handle === handle_) { + args.callback(toByteArray(native.getResultObject(event))); + } + }; + + return _bluetoothGATTCharacteristicListener.addListener(callback, callArgs); + }; + + BluetoothGATTCharacteristic.prototype.removeValueChangeListener = function() { + console.log('Entered BluetoothGATTCharacteristic.removeValueChangeListener()'); + + var args = AV.validateMethod(arguments, [{ + name: 'watchID', + type: AV.Types.LONG + }]); + + var callArgs = { handle: handle_, address : address_ }; + + return _bluetoothGATTCharacteristicListener.removeListener(args.watchID, callArgs); + }; }; + /** * Creates a manager for specified listener event. Manager handles multiple * registered listeners @@ -1713,21 +1748,29 @@ var BluetoothGATTCharacteristic = function(data, address) { * This function should have following signature: * void callback(listener, event); * @param {string} addListenerId - optional parameter. If specified, this native - * method will be called synchronously when first + * method will be called synchronously when * listener is added. * @param {string} removeListenerId - optional parameter. If specified, this native - * method will be called synchronously when last + * method will be called synchronously when * listener is removed. + * @param {bool} repeatNativeCall - optional parameter. If specified, the addListenerId + * and removeListenerId methods will be called synchronously + * each time listener is added/removed. Otherwise they are + * going to be called just once: when first listener is added + * and last listener is removed. * * @return {object} object which allows to add or remove callbacks for specified listener */ -function _multipleListenerBuilder(name, callback, addListenerId, removeListenerId) { +function _multipleListenerBuilder(name, callback, addListenerId, removeListenerId, repeatNativeCall) { var listenerName = name; var addId = addListenerId; var removeId = removeListenerId; var callbackFunction = callback; var listeners = {}; var nextId = 1; + var jsListenerRegistered = false; + var nativeListenerRegistered = false; + var repeatNativeListenerCall = repeatNativeCall; function innerCallback(event) { for (var watchId in listeners) { @@ -1737,35 +1780,39 @@ function _multipleListenerBuilder(name, callback, addListenerId, removeListenerI } } - function addListener(callback) { + function addListener(callback, args) { var id = ++nextId; - if (!listenerRegistered) { - if (addId) { - var result = native.callSync(addId, {}); - if (native.isFailure(result)) { - throw native.getErrorObject(result); - } + if (addId && (!nativeListenerRegistered || repeatNativeListenerCall)) { + var result = native.callSync(addId, args || {}); + if (native.isFailure(result)) { + throw native.getErrorObject(result); } + nativeListenerRegistered = true; + } + + if (!jsListenerRegistered) { native.addListener(listenerName, innerCallback); - listenerRegistered = true; + jsListenerRegistered = true; } listeners[id] = callback; return id; } - function removeListener(watchId) { + function removeListener(watchId, args) { if (listeners.hasOwnProperty(watchId)) { delete listeners[watchId]; } - if (listenerRegistered && T.isEmptyObject(listeners)) { - if (removeId) { - native.callSync(removeId, {}); - } + if (removeId && ((nativeListenerRegistered && T.isEmptyObject(listeners)) || repeatNativeListenerCall)) { + native.callSync(removeId, args || {}); + nativeListenerRegistered = false; + } + + if (jsListenerRegistered && T.isEmptyObject(listeners)) { native.removeListener(listenerName, innerCallback); - listenerRegistered = false; + jsListenerRegistered = false; } } @@ -1777,15 +1824,12 @@ function _multipleListenerBuilder(name, callback, addListenerId, removeListenerI var _bluetoothGATTCharacteristicListener = _multipleListenerBuilder( 'BluetoothGATTCharacteristicValueChangeListener', - function(listener, data) { - var d = []; - data.value.forEach(function(b) { - d.push(Converter.toByte(b)); - }); - listener(d); + function(listener, event) { + listener(event); }, 'BluetoothGATTCharacteristic_addValueChangeListener', - 'BluetoothGATTCharacteristic_removeValueChangeListener' + 'BluetoothGATTCharacteristic_removeValueChangeListener', + true ); var _bleConnectChangeListener = _multipleListenerBuilder( @@ -1797,30 +1841,6 @@ var _bleConnectChangeListener = _multipleListenerBuilder( 'BluetoothLEDevice_removeConnectStateChangeListener' ); -BluetoothGATTCharacteristic.prototype.addValueChangeListener = function() { - console.log('Entered BluetoothGATTCharacteristic.addValueChangeListener()'); - - xwalk.utils.checkPrivilegeAccess(Privilege.BLUETOOTH); - - var args = AV.validateMethod(arguments, [{ - name: 'callback', - type: AV.Types.FUNCTION - }]); - - return _bluetoothGATTCharacteristicListener.addListener(args.callback); -}; - -BluetoothGATTCharacteristic.prototype.removeValueChangeListener = function() { - console.log('Entered BluetoothGATTCharacteristic.removeValueChangeListener()'); - - var args = AV.validateMethod(arguments, [{ - name: 'watchID', - type: AV.Types.LONG - }]); - - return _bluetoothGATTCharacteristicListener.removeListener(args.watchID); -}; - //class BluetoothGATTDescriptor //////////////////////////////////////////////////// var BluetoothGATTDescriptor = function(address) { var handle_ = data.handle; diff --git a/src/bluetooth/bluetooth_gatt_service.cc b/src/bluetooth/bluetooth_gatt_service.cc index c29c898a..090174b9 100644 --- a/src/bluetooth/bluetooth_gatt_service.cc +++ b/src/bluetooth/bluetooth_gatt_service.cc @@ -49,6 +49,7 @@ const std::string kSignedWrite = "isSignedWrite"; const std::string kWritable = "isWritable"; const std::string kWriteNoResponse = "isWriteNoResponse"; +const std::string kOnValueChanged = "BluetoothGATTCharacteristicValueChangeListener"; bool IsProperty (int propertyBits, bt_gatt_property_e property) { return (propertyBits & property) == 0; @@ -65,6 +66,8 @@ BluetoothGATTService::~BluetoothGATTService() { LoggerD("Entered"); for (auto it : gatt_clients_) { + // unregister callback, ignore errors + bt_gatt_client_unset_characteristic_value_changed_cb(it.second); LoggerD("destroying client for address: %s", it.first.c_str()); bt_gatt_client_destroy(it.second); } @@ -438,5 +441,79 @@ void BluetoothGATTService::WriteValue(const picojson::value& args, } ReportSuccess(out); } + +void BluetoothGATTService::AddValueChangeListener(const picojson::value& args, + picojson::object& out) { + LoggerD("Entered"); + const auto& address = args.get("address").get(); + if (!IsStillConnected(address)) { + LoggerE("Device with address %s is no longer connected", address.c_str()); + ReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, + "Device is not connected"), &out); + return; + } + + bt_gatt_h handle = (bt_gatt_h)static_cast(args.get(kHandle).get()); + + int ret = bt_gatt_client_set_characteristic_value_changed_cb(handle, OnCharacteristicValueChanged, this); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_gatt_client_set_characteristic_value_changed_cb() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to register listener"), &out); + } else { + ReportSuccess(out); + } +} + +void BluetoothGATTService::RemoveValueChangeListener( + const picojson::value& args, picojson::object& out) { + LoggerD("Entered"); + const auto& address = args.get("address").get(); + if (!IsStillConnected(address)) { + LoggerE("Device with address %s is no longer connected", address.c_str()); + ReportError(PlatformResult(ErrorCode::INVALID_STATE_ERR, + "Device is not connected"), &out); + return; + } + + bt_gatt_h handle = (bt_gatt_h)static_cast(args.get(kHandle).get()); + + int ret = bt_gatt_client_unset_characteristic_value_changed_cb(handle); + + if (BT_ERROR_NONE != ret) { + LoggerE("bt_gatt_client_unset_characteristic_value_changed_cb() failed with: %d", ret); + ReportError(util::GetBluetoothError(ret, "Failed to unregister listener"), &out); + } else { + ReportSuccess(out); + } +} + +void BluetoothGATTService::OnCharacteristicValueChanged( + bt_gatt_h characteristic, char* value, int length, void* user_data) { + LoggerD("Entered, characteristic: [%p], len: [d], user_data: [%p]", characteristic, length, user_data); + + auto service = static_cast(user_data); + + if (!service) { + LoggerE("user_data is NULL"); + return; + } + + picojson::value result = picojson::value(picojson::object()); + picojson::object result_obj = result.get(); + + result_obj.insert(std::make_pair(kHandle, picojson::value((double)(long)characteristic))); + + picojson::value byte_array = picojson::value(picojson::array()); + picojson::array& byte_array_obj = byte_array.get(); + + for (size_t i = 0 ; i < length; ++i) { + byte_array_obj.push_back(picojson::value(std::to_string(value[i]))); + } + + ReportSuccess(byte_array, result_obj); + + service->instance_.FireEvent(kOnValueChanged, result); +} + } // namespace bluetooth } // namespace extension diff --git a/src/bluetooth/bluetooth_gatt_service.h b/src/bluetooth/bluetooth_gatt_service.h index c072af57..ac9b9548 100644 --- a/src/bluetooth/bluetooth_gatt_service.h +++ b/src/bluetooth/bluetooth_gatt_service.h @@ -43,6 +43,10 @@ class BluetoothGATTService { void GetCharacteristics(const picojson::value& data, picojson::object& out); void ReadValue(const picojson::value& args, picojson::object& out); void WriteValue(const picojson::value& args, picojson::object& out); + void AddValueChangeListener(const picojson::value& args, + picojson::object& out); + void RemoveValueChangeListener(const picojson::value& args, + picojson::object& out); private: bool IsStillConnected(const std::string& address); @@ -54,6 +58,10 @@ class BluetoothGATTService { const std::string& uuid, picojson::array* array); + static void OnCharacteristicValueChanged(bt_gatt_h characteristic, + char* value, int len, + void* user_data); + std::map gatt_clients_; BluetoothInstance& instance_; diff --git a/src/bluetooth/bluetooth_instance.cc b/src/bluetooth/bluetooth_instance.cc index d5bde2ba..5ac3edf7 100644 --- a/src/bluetooth/bluetooth_instance.cc +++ b/src/bluetooth/bluetooth_instance.cc @@ -140,6 +140,14 @@ BluetoothInstance::BluetoothInstance() : std::bind(&BluetoothGATTService::ReadValue, &bluetooth_gatt_service_, _1, _2)); REGISTER_SYNC("BluetoothGATT_writeValue", std::bind(&BluetoothGATTService::WriteValue, &bluetooth_gatt_service_, _1, _2)); + REGISTER_SYNC( + "BluetoothGATTCharacteristic_addValueChangeListener", + std::bind(&BluetoothGATTService::AddValueChangeListener, + &bluetooth_gatt_service_, _1, _2)); + REGISTER_SYNC( + "BluetoothGATTCharacteristic_removeValueChangeListener", + std::bind(&BluetoothGATTService::RemoveValueChangeListener, + &bluetooth_gatt_service_, _1, _2)); #undef REGISTER_ASYNC #undef REGISTER_SYNC