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