[BluetoothLE] Added listeners for characteristic.
authorPawel Andruszkiewicz <p.andruszkie@samsung.com>
Mon, 11 May 2015 09:48:25 +0000 (11:48 +0200)
committerPawel Andruszkiewicz <p.andruszkie@samsung.com>
Tue, 12 May 2015 07:07:11 +0000 (16:07 +0900)
Change-Id: Ia4ce7dd4e0806dbf8e33776d02bdaf73d88b899d
Signed-off-by: Pawel Andruszkiewicz <p.andruszkie@samsung.com>
src/bluetooth/bluetooth_api.js
src/bluetooth/bluetooth_gatt_service.cc
src/bluetooth/bluetooth_gatt_service.h
src/bluetooth/bluetooth_instance.cc

index 8376294ca0b2a2d7a71b0c9dcd40f42e09618af1..472e193aa4cd77796009e99d908a05e5be5e276c 100644 (file)
@@ -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;
index c29c898a881e4e003ac37068b021a69ac8f19b08..090174b9dc23f0fd3a039296603a374bdf489e6d 100644 (file)
@@ -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<std::string>();
+  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<long>(args.get(kHandle).get<double>());
+
+  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<std::string>();
+  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<long>(args.get(kHandle).get<double>());
+
+  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<BluetoothGATTService*>(user_data);
+
+  if (!service) {
+    LoggerE("user_data is NULL");
+    return;
+  }
+
+  picojson::value result = picojson::value(picojson::object());
+  picojson::object result_obj = result.get<picojson::object>();
+
+  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<picojson::array>();
+
+  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
index c072af57cff1e4a44049684beed9a0630885cfb3..ac9b9548013ad7eac3ba32aa4ed7f136bb5bda12 100644 (file)
@@ -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<std::string, bt_gatt_client_h> gatt_clients_;
 
   BluetoothInstance& instance_;
index d5bde2baf670bfef7549c521ac1c56380e3c30fe..5ac3edf7bfb355ba631723cd968f0f5fbcd19824 100644 (file)
@@ -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