[Bluetooth] Implement BluetoothGATTServerCharacteristic::notifyAboutValueChange() 94/242894/12
authorDawid Juszczak <d.juszczak@samsung.com>
Tue, 1 Sep 2020 18:43:19 +0000 (20:43 +0200)
committerDawid Juszczak <d.juszczak@samsung.com>
Fri, 18 Sep 2020 10:52:39 +0000 (12:52 +0200)
https://code.sec.samsung.net/jira/browse/TWDAPI-263

[Verification]
TODO

Change-Id: Id8e09ed2adee2c1e400c0979672569ae6f3f49c2
Signed-off-by: Dawid Juszczak <d.juszczak@samsung.com>
src/bluetooth/bluetooth_api.js
src/bluetooth/bluetooth_gatt_server.cc
src/bluetooth/bluetooth_gatt_server.h
src/bluetooth/bluetooth_gatt_server_service.cc
src/bluetooth/bluetooth_gatt_server_service.h
src/bluetooth/bluetooth_instance.cc
src/bluetooth/bluetooth_instance.h

index 290a411..fc361a5 100755 (executable)
@@ -293,7 +293,7 @@ tizen.BluetoothLEAdvertiseData = function(dict) {
                 if (T.isNull(v)) {
                     servicesData_ = v;
                 } else if (T.isArray(v)) {
-                    var tmpArray = []
+                    var tmpArray = [];
                     for (var i = 0; i < v.length; ++i) {
                         if (v[i] instanceof tizen.BluetoothLEServiceData) {
                             tmpArray.push(v[i]);
@@ -2483,6 +2483,73 @@ var BluetoothGATTServerCharacteristic = function(data) {
     this.removeValueChangeListener = function() {
         /* Intended no operation */
     };
+
+    this.notifyAboutValueChange = function(
+        value,
+        clientAddress,
+        notificationCB,
+        errorCB
+    ) {
+        var args = AV.validateArgs(
+            Array.prototype.slice.call(arguments, 1),
+            [
+                {
+                    name: 'clientAddress',
+                    type: AV.Types.STRING,
+                    optional: true,
+                    nullable: true
+                },
+                {
+                    name: 'notificationCB',
+                    type: AV.Types.LISTENER,
+                    values: [
+                        'onnotificationsuccess',
+                        'onnotificationfail',
+                        'onnotificationfinish'
+                    ],
+                    optional: true,
+                    nullable: true
+                },
+                {
+                    name: 'errorCB',
+                    type: AV.Types.FUNCTION,
+                    optional: true,
+                    nullable: true
+                }
+            ]
+        );
+
+        var callArgs = {
+            value: BluetoothManager_toByteArray(value),
+            client: args.clientAddress || null,
+            _id: this._id,
+            notifyId: _BluetoothGATTServerCharacteristicNotifyId++
+        };
+
+        var callback = function(result) {
+            if (native.isFailure(result)) {
+                native.callIfPossible(args.errorCB, native.getErrorObject(result));
+            }
+        };
+
+        var result = native.call(
+            'BluetoothGATTServerCharacteristicNotifyAboutValueChange',
+            callArgs,
+            callback
+        );
+
+        if (native.isFailure(result)) {
+            throw native.getErrorObject(result);
+        }
+
+        native.addListener(
+            'BluetoothGATTServerCharacteristicNotifyCallback_' + callArgs.notifyId,
+            _BluetoothGATTServerCharacteristicNotifyCallback
+        );
+
+        _BluetoothGATTServerCharacteristicNotifyListeners[callArgs.notifyId] =
+            args.notificationCB;
+    };
 };
 
 BluetoothGATTServerCharacteristic.prototype = Object.create(
@@ -2681,7 +2748,7 @@ var _setReadValueRequestCallbackCommon = function() {
                 }
             );
             _BluetoothGATTServerReadWriteValueRequestCallbacks[
-                "ReadValueCallback" + entityId
+                'ReadValueCallback' + entityId
             ] = readValueRequestCallback;
             native.callIfPossible(args.successCallback, native.getErrorObject(result));
         }
@@ -2770,6 +2837,44 @@ var _setWriteValueRequestCallbackCommon = function() {
 BluetoothGATTServerCharacteristic.prototype.setReadValueRequestCallback = _setReadValueRequestCallbackCommon;
 BluetoothGATTServerCharacteristic.prototype.setWriteValueRequestCallback = _setWriteValueRequestCallbackCommon;
 
+var _BluetoothGATTServerCharacteristicNotifyId = 0;
+var _BluetoothGATTServerCharacteristicNotifyListeners = {};
+var _BluetoothGATTServerCharacteristicNotifyCallback = function(event) {
+    privUtils_.log('Got notification about characteristic\'s value change');
+    if (
+        T.isNullOrUndefined(
+            _BluetoothGATTServerCharacteristicNotifyListeners[event.notifyId]
+        )
+    ) {
+        privUtils_.log('Notification callback is not set, skipping');
+        return;
+    }
+
+    var callback = _BluetoothGATTServerCharacteristicNotifyListeners[event.notifyId];
+
+    if (event.type === 'onnotificationsuccess') {
+        native.callIfPossible(callback.onnotificationsuccess, event.clientAddress);
+        return;
+    }
+
+    if (event.type === 'onnotificationfail') {
+        native.callIfPossible(
+            callback.onnotificationfail,
+            event.clientAddress,
+            native.getErrorObject(event)
+        );
+        return;
+    }
+
+    if (event.type === 'onnotificationfinish') {
+        native.callIfPossible(callback.onnotificationfinish, event.clientAddress);
+    }
+
+    native.removeListener(
+        'BluetoothGATTServerCharacteristicNotifyCallback_' + event.notifyId
+    );
+};
+
 /**
  * Creates a manager for specified listener event. Manager handles multiple
  * registered listeners
index d1a4bd6..5115539 100644 (file)
@@ -236,6 +236,25 @@ bool BluetoothGATTServer::IsRunning() {
   return running_;
 }
 
+void BluetoothGATTServer::NotifyAboutValueChange(const picojson::value& args,
+                                                 picojson::object& out) {
+  ScopeLogger();
+
+  if (!initialized_) {
+    LoggerE("Server not started");
+    ReportError(common::PlatformResult{common::ErrorCode::ABORT_ERR, "Server not started"}, &out);
+    return;
+  }
+
+  auto result = service_.NotifyAboutValueChange(args);
+  if (result.IsError()) {
+    ReportError(result, &out);
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
 PlatformResult BluetoothGATTServer::Initialize() {
   ScopeLogger();
 
index 9dc72be..fa96d5d 100644 (file)
@@ -44,6 +44,7 @@ class BluetoothGATTServer {
   void SetWriteValueRequestCallback(const picojson::value& args, picojson::object& out);
   void SendResponse(const picojson::value& args, picojson::object& out);
   bool IsRunning();
+  void NotifyAboutValueChange(const picojson::value& args, picojson::object& out);
 
   static bool DestroyService(int total, int index, bt_gatt_h handle, void* user_data);
   static bool DestroyCharacteristic(int total, int index, bt_gatt_h handle, void* user_data);
index 07aeb58..3939c3a 100644 (file)
@@ -70,6 +70,12 @@ const std::string kStatusCode = "statusCode";
 const std::string kData = "data";
 const std::string kReadCallback = "readCallback";
 const std::string kWriteCallback = "writeCallback";
+const std::string kNotifyId = "notifyId";
+const std::string kOnNotificationFail = "onnotificationfail";
+const std::string kOnNotificationFinish = "onnotificationfinish";
+const std::string kOnNotificationSuccess = "onnotificationsuccess";
+const std::string kType = "type";
+const std::string kClient = "client";
 
 int GetPermissionsInt(const picojson::value& permissions) {
   ScopeLogger("permissions: %s", permissions.serialize().c_str());
@@ -513,8 +519,8 @@ PlatformResult BluetoothGATTServerService::SetWriteValueRequestCallback(
     auto remote_address_copy = std::string{remote_address};
     auto value_copy = std::string{value};
     rw_callback_data->instance_.GetWorker().add_job([remote_address_copy, request_id, server,
-                                                     gatt_handle, response_needed, offset, value_copy,
-                                                     rw_callback_data] {
+                                                     gatt_handle, response_needed, offset,
+                                                     value_copy, rw_callback_data] {
       ScopeLogger("Async call: SetWriteValueRequestCallback");
 
       auto write_value_request = picojson::value{picojson::object{}};
@@ -527,7 +533,7 @@ PlatformResult BluetoothGATTServerService::SetWriteValueRequestCallback(
 
       write_value_request_obj[kValue] = picojson::value{picojson::array{}};
       auto& value_byte_array = write_value_request_obj[kValue].get<picojson::array>();
-      for (auto c: value_copy) {
+      for (auto c : value_copy) {
         value_byte_array.push_back(picojson::value{static_cast<double>(c)});
       }
       const auto callback_name =
@@ -590,5 +596,99 @@ PlatformResult BluetoothGATTServerService::SendResponse(const picojson::value& a
   return common::PlatformResult{};
 }
 
+struct NotificationUserData {
+  int notify_id;
+  BluetoothGATTServerService* service;
+};
+
+void BluetoothGATTServerService::NotifyCallback(int result, const char* remote_address,
+                                                bt_gatt_server_h server, bt_gatt_h characteristic,
+                                                bool completed, void* user_data) {
+  ScopeLogger("Async callback of bt_gatt_server_notify_characteristic_changed_value()");
+  LoggerD("result: %d, remote_address: %s, completed: %d", result, remote_address, completed);
+
+  NotificationUserData* data = static_cast<NotificationUserData*>(user_data);
+  BluetoothGATTServerService* bt_service = data->service;
+  int notify_id = data->notify_id;
+  LoggerD("notify_id: %d", notify_id);
+
+  picojson::value response = picojson::value(picojson::object());
+  picojson::object& obj = response.get<picojson::object>();
+
+  if (remote_address != nullptr) {
+    obj.insert(std::make_pair(kClientAddress, remote_address));
+  } else {
+    obj.insert(std::make_pair(kClientAddress, picojson::value()));
+  }
+  obj.insert(std::make_pair(kNotifyId, static_cast<double>(notify_id)));
+
+  std::string type;
+  if (BT_ERROR_NONE != result) {
+    auto ret = util::GetBluetoothError(result, "Failed to notify client about change");
+    type = kOnNotificationFail;
+    common::tools::ReportError(ret, &obj);
+  } else {
+    type = kOnNotificationSuccess;
+  }
+
+  obj.insert(std::make_pair(kType, type));
+
+  LoggerD("Sending event: type: %s, notifyId: %d", type.c_str(), notify_id);
+  std::string event =
+      "BluetoothGATTServerCharacteristicNotifyCallback_" + std::to_string(notify_id);
+  bt_service->instance_.FireEvent(event, response);
+
+  if (completed) {
+    obj[kType] = picojson::value(kOnNotificationFinish);
+    bt_service->instance_.FireEvent(event, response);
+    delete data;
+  }
+};
+
+common::PlatformResult BluetoothGATTServerService::NotifyAboutValueChange(
+    const picojson::value& args) {
+  ScopeLogger();
+
+  auto gatt_id = static_cast<int>(args.get(kId).get<double>());
+  auto gatt_iter = gatt_objects_.find(gatt_id);
+  if (gatt_iter == gatt_objects_.end()) {
+    LoggerE("Can't find gatt object with id: %d", gatt_id);
+    return common::PlatformResult{common::ErrorCode::ABORT_ERR, "Unknown error occurred"};
+  }
+  bt_gatt_h gatt_handle = gatt_iter->second;
+  LoggerD("Found proper gatt object");
+
+  const picojson::array& value_array = args.get(kValue).get<picojson::array>();
+  int value_size = value_array.size();
+  std::unique_ptr<char[]> value_data(new char[value_size]);
+  for (int i = 0; i < value_size; ++i) {
+    value_data[i] = static_cast<int>(value_array[i].get<double>());
+  }
+  int ret = bt_gatt_set_value(gatt_handle, value_data.get(), value_size);
+  if (BT_ERROR_NONE != ret) {
+    LoggerE("Failed to set value, error: %d, message: %s", ret, get_error_message(ret));
+    return common::PlatformResult{common::ErrorCode::ABORT_ERR, "Unknown error occurred"};
+  }
+  LoggerD("New value set in characteristic");
+
+  const char* address = args.get(kClient).is<picojson::null>()
+                            ? nullptr
+                            : args.get(kClient).get<std::string>().c_str();
+
+  LoggerD("Client address to notify: [%s]", address);
+
+  auto notify_id = static_cast<int>(args.get(kNotifyId).get<double>());
+  NotificationUserData* user_data = new NotificationUserData{notify_id, this};
+  ret = bt_gatt_server_notify_characteristic_changed_value(gatt_handle, NotifyCallback, address,
+                                                           user_data);
+  if (BT_ERROR_NONE != ret) {
+    LoggerE("bt_gatt_server_notify_characteristic_changed_value() failed: %d", ret);
+    delete user_data;
+    return common::PlatformResult{common::ErrorCode::ABORT_ERR, "Failed to notify"};
+  }
+  LoggerD("Sent notification about value changed");
+  return common::PlatformResult{};
+}
+
 }  // namespace bluetooth
 }  // namespace extension
index 9321d1c..5ed008c 100644 (file)
@@ -46,13 +46,13 @@ class BluetoothGATTServerService {
   PlatformResult SetReadValueRequestCallback(const picojson::value& args);
   PlatformResult SetWriteValueRequestCallback(const picojson::value& args);
   PlatformResult SendResponse(const picojson::value& args);
+  PlatformResult NotifyAboutValueChange(const picojson::value& args);
 
  private:
+  BluetoothInstance& instance_;
   std::map<int, bt_gatt_h> gatt_objects_;
   std::map<std::pair<bt_gatt_h, std::string>, std::string> callback_names_;
 
-  BluetoothInstance& instance_;
-
   struct ReadWriteRequestCallbackData {
     BluetoothInstance& instance_;
     std::map<std::pair<bt_gatt_h, std::string>, std::string>& callback_names_map_;
@@ -61,6 +61,8 @@ class BluetoothGATTServerService {
   static bool DestroyService(int total, int index, bt_gatt_h handle, void* user_data);
   static bool DestroyCharacteristic(int total, int index, bt_gatt_h handle, void* user_data);
   static bool DestroyDescriptor(int total, int index, bt_gatt_h handle, void* user_data);
+  static void NotifyCallback(int result, const char* remote_address, bt_gatt_server_h server,
+                             bt_gatt_h characteristic, bool completed, void* user_data);
 
   PlatformResult AddDescriptors(const picojson::array& descriptors, bt_gatt_h characteristic_handle,
                                 std::vector<std::pair<int, bt_gatt_h>>& new_gatt_objects);
index ad0e2b0..b4eab38 100644 (file)
@@ -115,6 +115,7 @@ BluetoothInstance::BluetoothInstance()
   REGISTER_METHOD(BluetoothGATTServerSetReadValueRequestCallback);
   REGISTER_METHOD(BluetoothGATTServerSetWriteValueRequestCallback);
   REGISTER_METHOD(BluetoothGATTServerSendResponse);
+  REGISTER_METHOD(BluetoothGATTServerCharacteristicNotifyAboutValueChange);
 
 #undef REGISTER_METHOD
 }
@@ -642,5 +643,23 @@ void BluetoothInstance::BluetoothGATTServerSendResponse(const picojson::value& a
   ReportSuccess(out);
 }
 
+void BluetoothInstance::BluetoothGATTServerCharacteristicNotifyAboutValueChange(
+    const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+  CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out);
+
+  worker.add_job([this, args] {
+    ScopeLogger("Async call: BluetoothGATTServerCharacteristicNotifyAboutValueChange");
+    picojson::value response = picojson::value(picojson::object());
+    picojson::object& async_out = response.get<picojson::object>();
+    double callback_id = args.get(JSON_CALLBACK_ID).get<double>();
+    async_out[JSON_CALLBACK_ID] = picojson::value(callback_id);
+    this->bluetooth_gatt_server_.NotifyAboutValueChange(args, async_out);
+    this->PostMessage(response.serialize().c_str());
+  });
+
+  ReportSuccess(out);
+}
+
 }  // namespace bluetooth
 }  // namespace extension
index a7043b2..fc02598 100644 (file)
@@ -129,6 +129,8 @@ class BluetoothInstance : public common::ParsedInstance {
   void BluetoothGATTServerSetWriteValueRequestCallback(const picojson::value& args,
                                                        picojson::object& out);
   void BluetoothGATTServerSendResponse(const picojson::value& args, picojson::object& out);
+  void BluetoothGATTServerCharacteristicNotifyAboutValueChange(const picojson::value& args,
+                                                               picojson::object& out);
 
   BluetoothAdapter bluetooth_adapter_;
   BluetoothDevice bluetooth_device_;