From: Dawid Juszczak Date: Tue, 1 Sep 2020 18:43:19 +0000 (+0200) Subject: [Bluetooth] Implement BluetoothGATTServerCharacteristic::notifyAboutValueChange() X-Git-Tag: submit/tizen/20200918.124009~1 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9f32c8dfd4a28258dddd9c279210bfe6414328b3;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Bluetooth] Implement BluetoothGATTServerCharacteristic::notifyAboutValueChange() https://code.sec.samsung.net/jira/browse/TWDAPI-263 [Verification] TODO Change-Id: Id8e09ed2adee2c1e400c0979672569ae6f3f49c2 Signed-off-by: Dawid Juszczak --- diff --git a/src/bluetooth/bluetooth_api.js b/src/bluetooth/bluetooth_api.js index 290a4111..fc361a5a 100755 --- a/src/bluetooth/bluetooth_api.js +++ b/src/bluetooth/bluetooth_api.js @@ -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 diff --git a/src/bluetooth/bluetooth_gatt_server.cc b/src/bluetooth/bluetooth_gatt_server.cc index d1a4bd67..51155392 100644 --- a/src/bluetooth/bluetooth_gatt_server.cc +++ b/src/bluetooth/bluetooth_gatt_server.cc @@ -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(); diff --git a/src/bluetooth/bluetooth_gatt_server.h b/src/bluetooth/bluetooth_gatt_server.h index 9dc72be2..fa96d5da 100644 --- a/src/bluetooth/bluetooth_gatt_server.h +++ b/src/bluetooth/bluetooth_gatt_server.h @@ -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); diff --git a/src/bluetooth/bluetooth_gatt_server_service.cc b/src/bluetooth/bluetooth_gatt_server_service.cc index 07aeb582..3939c3ae 100644 --- a/src/bluetooth/bluetooth_gatt_server_service.cc +++ b/src/bluetooth/bluetooth_gatt_server_service.cc @@ -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(); - for (auto c: value_copy) { + for (auto c : value_copy) { value_byte_array.push_back(picojson::value{static_cast(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(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(); + + 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(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(args.get(kId).get()); + 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(); + int value_size = value_array.size(); + std::unique_ptr value_data(new char[value_size]); + for (int i = 0; i < value_size; ++i) { + value_data[i] = static_cast(value_array[i].get()); + } + 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() + ? nullptr + : args.get(kClient).get().c_str(); + + LoggerD("Client address to notify: [%s]", address); + + auto notify_id = static_cast(args.get(kNotifyId).get()); + 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 diff --git a/src/bluetooth/bluetooth_gatt_server_service.h b/src/bluetooth/bluetooth_gatt_server_service.h index 9321d1c3..5ed008cd 100644 --- a/src/bluetooth/bluetooth_gatt_server_service.h +++ b/src/bluetooth/bluetooth_gatt_server_service.h @@ -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 gatt_objects_; std::map, std::string> callback_names_; - BluetoothInstance& instance_; - struct ReadWriteRequestCallbackData { BluetoothInstance& instance_; std::map, 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>& new_gatt_objects); diff --git a/src/bluetooth/bluetooth_instance.cc b/src/bluetooth/bluetooth_instance.cc index ad0e2b03..b4eab38e 100644 --- a/src/bluetooth/bluetooth_instance.cc +++ b/src/bluetooth/bluetooth_instance.cc @@ -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(); + double callback_id = args.get(JSON_CALLBACK_ID).get(); + 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 diff --git a/src/bluetooth/bluetooth_instance.h b/src/bluetooth/bluetooth_instance.h index a7043b2e..fc025980 100644 --- a/src/bluetooth/bluetooth_instance.h +++ b/src/bluetooth/bluetooth_instance.h @@ -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_;