From: Arkadiusz Pietraszek Date: Thu, 10 Sep 2020 16:59:10 +0000 (+0200) Subject: [Bluetooth] Implement BluetoothGATTServer{Characteristic|Descriptor}::setWriteValueRe... X-Git-Tag: submit/tizen/20200918.124009~5 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=8f8bab739f3f67cafac2e1c6232e9cc5068b0991;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Bluetooth] Implement BluetoothGATTServer{Characteristic|Descriptor}::setWriteValueRequestCallback [ACR] https://code.sec.samsung.net/jira/browse/TWDAPI-263 [Verification] Basic use cases were tested in Chrome Dev Tools and run properly. More thorough verification should yet be done. Change-Id: I676dd8f06f8a9b19c2c98ffeaf21cfc9e3fd19d3 Signed-off-by: Arkadiusz Pietraszek Signed-off-by: Pawel Wasowski --- diff --git a/src/bluetooth/bluetooth_api.js b/src/bluetooth/bluetooth_api.js index 6bfcbec7..9a5ed9e0 100755 --- a/src/bluetooth/bluetooth_api.js +++ b/src/bluetooth/bluetooth_api.js @@ -2544,6 +2544,63 @@ function _createReadValueRequestCallback( ); } +function _createWriteValueRequestCallback( + _id, + sendResponseSuccessCallback, + sendResponseErrorCallback +) { + return _singleListenerBuilder( + 'WriteValueRequestCallback_' + _id, + /* + * _singleListenerBuilder requires 2 callbacks, the second of which + * is an error callback. + * Write value request events coming from the native layer + * are never errors. + * Hence, we don't use the second callback here. + */ + function(event, writeValueRequestCallback, unusedErrorCallback) { + var clientAddress = event.clientAddress; + var value = event.value; + var offset = event.offset; + var replyRequired = event.replyRequired; + + if (writeValueRequestCallback) { + var requestReply = writeValueRequestCallback( + clientAddress, + BluetoothManager_toByteArray(value), + offset, + replyRequired + ); + var response = { + _id: _id, + requestId: event.requestId, + requestType: event.requestType, + value: null, // Responses to write requests don't contain value + offset: offset, + statusCode: requestReply.statusCode, + data: requestReply.data + }; + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible( + sendResponseErrorCallback, + native.getErrorObject(result) + ); + } else { + native.callIfPossible(sendResponseSuccessCallback); + } + }; + var result = native.call( + 'BluetoothGATTServerSendResponse', + response, + callback + ); + } + return true; + } + ); +} + var _BluetoothGATTServerReadWriteValueRequestCallbacks = {}; var _setReadValueRequestCallbackCommon = function() { @@ -2614,7 +2671,76 @@ var _setReadValueRequestCallbackCommon = function() { } }; +var _setWriteValueRequestCallbackCommon = function() { + var args = AV.validateArgs(arguments, [ + { + name: 'writeValueRequestCallback', + type: AV.Types.FUNCTION + }, + { + name: 'successCallback', + type: AV.Types.FUNCTION, + optional: true, + nullable: true + }, + { + name: 'errorCallback', + type: AV.Types.FUNCTION, + optional: true, + nullable: true + }, + { + name: 'sendResponseSuccessCallback', + type: AV.Types.FUNCTION, + optional: true, + nullable: true + }, + { + name: 'sendResponseErrorCallback', + type: AV.Types.FUNCTION, + optional: true, + nullable: true + } + ]); + + var entityId = this._id; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + var writeValueRequestCallback = _createWriteValueRequestCallback( + entityId, + args.sendResponseSuccessCallback, + args.sendResponseErrorCallback + ); + writeValueRequestCallback.addListener( + args.writeValueRequestCallback, + function unusedErrorCallback() { + /* Intentionally no operation */ + } + ); + _BluetoothGATTServerReadWriteValueRequestCallbacks[ + 'WriteValueCallback' + entityId + ] = writeValueRequestCallback; + native.callIfPossible(args.successCallback, native.getErrorObject(result)); + } + }; + + var callArgs = { _id: this._id }; + var result = native.call( + 'BluetoothGATTServerSetWriteValueRequestCallback', + callArgs, + callback + ); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } +}; + BluetoothGATTServerCharacteristic.prototype.setReadValueRequestCallback = _setReadValueRequestCallbackCommon; +BluetoothGATTServerCharacteristic.prototype.setWriteValueRequestCallback = _setWriteValueRequestCallbackCommon; /** * Creates a manager for specified listener event. Manager handles multiple @@ -2982,6 +3108,7 @@ Object.defineProperty(BluetoothGATTServerDescriptor.prototype, 'constructor', { }); BluetoothGATTServerDescriptor.prototype.setReadValueRequestCallback = _setReadValueRequestCallbackCommon; +BluetoothGATTServerDescriptor.prototype.setWriteValueRequestCallback = _setWriteValueRequestCallbackCommon; // class BluetoothAdapter /////////////////////////// var BluetoothAdapter = function() { diff --git a/src/bluetooth/bluetooth_gatt_server.cc b/src/bluetooth/bluetooth_gatt_server.cc index b0fd2e43..2c7bb9f5 100644 --- a/src/bluetooth/bluetooth_gatt_server.cc +++ b/src/bluetooth/bluetooth_gatt_server.cc @@ -205,6 +205,20 @@ void BluetoothGATTServer::SetReadValueRequestCallback(const picojson::value& arg ReportSuccess(out); } +void BluetoothGATTServer::SetWriteValueRequestCallback(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + auto result = service_.SetWriteValueRequestCallback(args); + + if (result.IsError()) { + ReportError(result, &out); + return; + } + + ReportSuccess(out); +} + void BluetoothGATTServer::SendResponse(const picojson::value& args, picojson::object& out) { ScopeLogger(); diff --git a/src/bluetooth/bluetooth_gatt_server.h b/src/bluetooth/bluetooth_gatt_server.h index b3cd1507..770d83fa 100644 --- a/src/bluetooth/bluetooth_gatt_server.h +++ b/src/bluetooth/bluetooth_gatt_server.h @@ -41,6 +41,7 @@ class BluetoothGATTServer { void RegisterService(const picojson::value& args, picojson::object& out); void UnregisterService(const picojson::value& args, picojson::object& out); void SetReadValueRequestCallback(const picojson::value& args, picojson::object& out); + void SetWriteValueRequestCallback(const picojson::value& args, picojson::object& out); void SendResponse(const picojson::value& args, picojson::object& out); bool IsRunning(); diff --git a/src/bluetooth/bluetooth_gatt_server_service.cc b/src/bluetooth/bluetooth_gatt_server_service.cc index bf07067a..07aeb582 100644 --- a/src/bluetooth/bluetooth_gatt_server_service.cc +++ b/src/bluetooth/bluetooth_gatt_server_service.cc @@ -60,12 +60,16 @@ const std::string kId = "_id"; const std::string kIdsToRemoveFromNativeLayer = "idsToRemoveFromNativeLayer"; const std::string kClientAddress = "clientAddress"; const std::string kRequestId = "requestId"; +const std::string kValue = "value"; const std::string kOffset = "offset"; +const std::string kReplyRequired = "replyRequired"; const std::string kRequestType = "requestType"; const std::string kReadRequestType = "readRequestType"; const std::string kWriteRequestType = "writeRequestType"; const std::string kStatusCode = "statusCode"; const std::string kData = "data"; +const std::string kReadCallback = "readCallback"; +const std::string kWriteCallback = "writeCallback"; int GetPermissionsInt(const picojson::value& permissions) { ScopeLogger("permissions: %s", permissions.serialize().c_str()); @@ -425,7 +429,8 @@ common::PlatformResult BluetoothGATTServerService::UnregisterService(const picoj auto handle_to_remove = gatt_objects_[id_to_remove]; LoggerD("Erasing gatt object with _id: %d", id_to_remove); - callback_names_.erase(handle_to_remove); + callback_names_.erase(std::make_pair(handle_to_remove, kReadCallback)); + callback_names_.erase(std::make_pair(handle_to_remove, kWriteCallback)); gatt_objects_.erase(id_to_remove); } @@ -440,7 +445,8 @@ PlatformResult BluetoothGATTServerService::SetReadValueRequestCallback( LoggerD("Setting read value request callback for a GATT entity with _id: %d", _id); auto gatt_handle = gatt_objects_[_id]; - callback_names_[gatt_handle] = "ReadValueRequestCallback_" + std::to_string(_id); + callback_names_[std::make_pair(gatt_handle, kReadCallback)] = + "ReadValueRequestCallback_" + std::to_string(_id); auto read_value_request_callback = [](const char* remote_address, int request_id, bt_gatt_server_h server, bt_gatt_h gatt_handle, int offset, @@ -463,7 +469,8 @@ PlatformResult BluetoothGATTServerService::SetReadValueRequestCallback( read_value_request_obj[kRequestId] = picojson::value{static_cast(request_id)}; read_value_request_obj[kRequestType] = picojson::value{kReadRequestType}; read_value_request_obj[kOffset] = picojson::value{static_cast(offset)}; - const auto callback_name = rw_callback_data->callback_names_map_[gatt_handle]; + const auto callback_name = + rw_callback_data->callback_names_map_[std::make_pair(gatt_handle, kReadCallback)]; LoggerD("Firing read value request event: %s: %s", callback_name.c_str(), read_value_request.serialize().c_str()); rw_callback_data->instance_.FireEvent(callback_name, read_value_request); @@ -482,9 +489,70 @@ PlatformResult BluetoothGATTServerService::SetReadValueRequestCallback( return common::PlatformResult{}; } -PlatformResult BluetoothGATTServerService::SendResponse(const picojson::value& args) { +PlatformResult BluetoothGATTServerService::SetWriteValueRequestCallback( + const picojson::value& args) { ScopeLogger(); + auto _id = static_cast(args.get(kId).get()); + LoggerD("Setting write value request callback for a GATT entity with _id: %d", _id); + + auto gatt_handle = gatt_objects_[_id]; + callback_names_[std::make_pair(gatt_handle, kWriteCallback)] = + "WriteValueRequestCallback_" + std::to_string(_id); + + auto write_value_request_callback = []( + const char* remote_address, int request_id, bt_gatt_server_h server, bt_gatt_h gatt_handle, + bool response_needed, int offset, const char* value, int len, void* user_data) -> void { + ScopeLogger( + "WriteValueRequestCallback called. remote_address: %s, request_id: %d, response_needed: " + "%d, offset: %d, value: %s, len: %d", + remote_address, request_id, response_needed, offset, value, len); + auto rw_callback_data = static_cast(user_data); + + // We create a copy of value and remote_address + 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] { + ScopeLogger("Async call: SetWriteValueRequestCallback"); + + auto write_value_request = picojson::value{picojson::object{}}; + auto& write_value_request_obj = write_value_request.get(); + write_value_request_obj[kClientAddress] = picojson::value{remote_address_copy}; + write_value_request_obj[kRequestId] = picojson::value{static_cast(request_id)}; + write_value_request_obj[kRequestType] = picojson::value{kWriteRequestType}; + write_value_request_obj[kReplyRequired] = picojson::value{response_needed}; + write_value_request_obj[kOffset] = picojson::value{static_cast(offset)}; + + write_value_request_obj[kValue] = picojson::value{picojson::array{}}; + auto& value_byte_array = write_value_request_obj[kValue].get(); + for (auto c: value_copy) { + value_byte_array.push_back(picojson::value{static_cast(c)}); + } + const auto callback_name = + rw_callback_data->callback_names_map_[std::make_pair(gatt_handle, kWriteCallback)]; + LoggerD("Firing write value request event: %s: %s", callback_name.c_str(), + write_value_request.serialize().c_str()); + rw_callback_data->instance_.FireEvent(callback_name, write_value_request); + }); + }; + + auto ret = bt_gatt_server_set_write_value_requested_cb(gatt_handle, write_value_request_callback, + &rw_request_callback_data_); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_gatt_server_set_write_value_requested_cb() failed: %d (%s)", ret, + get_error_message(ret)); + return BluetoothErrorToPlatformResult(ret, "Failed to set write value request callback"); + } + + LoggerD("bt_gatt_server_set_write_value_requested_cb(): SUCCESS"); + return common::PlatformResult{}; +} + +PlatformResult BluetoothGATTServerService::SendResponse(const picojson::value& args) { + ScopeLogger("Response: %s", args.serialize().c_str()); + auto _id = static_cast(args.get(kId).get()); auto request_id = static_cast(args.get(kRequestId).get()); auto request_type_str = args.get(kRequestType).get(); @@ -508,7 +576,7 @@ PlatformResult BluetoothGATTServerService::SendResponse(const picojson::value& a LoggerD( "Sending response: _id: %d, requestId: %d, requestType: %s, offset: %d, statusCode: %d, " - "value: [%s], value size: %d", + "value: [%p], value size: %d", _id, request_id, request_type_str.c_str(), offset, status_code, value.get(), value_size); auto ret = bt_gatt_server_send_response(request_id, request_type, offset, status_code, diff --git a/src/bluetooth/bluetooth_gatt_server_service.h b/src/bluetooth/bluetooth_gatt_server_service.h index e53916b3..9321d1c3 100644 --- a/src/bluetooth/bluetooth_gatt_server_service.h +++ b/src/bluetooth/bluetooth_gatt_server_service.h @@ -44,17 +44,18 @@ class BluetoothGATTServerService { const std::vector>& new_gatt_objects); PlatformResult UnregisterService(const picojson::value& args, bt_gatt_server_h server); PlatformResult SetReadValueRequestCallback(const picojson::value& args); + PlatformResult SetWriteValueRequestCallback(const picojson::value& args); PlatformResult SendResponse(const picojson::value& args); private: std::map gatt_objects_; - std::map callback_names_; + std::map, std::string> callback_names_; BluetoothInstance& instance_; struct ReadWriteRequestCallbackData { BluetoothInstance& instance_; - std::map& callback_names_map_; + std::map, std::string>& callback_names_map_; } rw_request_callback_data_; static bool DestroyService(int total, int index, bt_gatt_h handle, void* user_data); diff --git a/src/bluetooth/bluetooth_instance.cc b/src/bluetooth/bluetooth_instance.cc index 1366972c..ad0e2b03 100644 --- a/src/bluetooth/bluetooth_instance.cc +++ b/src/bluetooth/bluetooth_instance.cc @@ -113,6 +113,7 @@ BluetoothInstance::BluetoothInstance() REGISTER_METHOD(BluetoothGATTServerRegisterService); REGISTER_METHOD(BluetoothGATTServerUnregisterService); REGISTER_METHOD(BluetoothGATTServerSetReadValueRequestCallback); + REGISTER_METHOD(BluetoothGATTServerSetWriteValueRequestCallback); REGISTER_METHOD(BluetoothGATTServerSendResponse); #undef REGISTER_METHOD @@ -605,6 +606,24 @@ void BluetoothInstance::BluetoothGATTServerSetReadValueRequestCallback(const pic ReportSuccess(out); } +void BluetoothInstance::BluetoothGATTServerSetWriteValueRequestCallback(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out); + + worker.add_job([this, args] { + ScopeLogger("Async call: BluetoothGATTServerSetWriteValueRequestCallback"); + picojson::value response = picojson::value(picojson::object()); + picojson::object& async_out = response.get(); + double callback_id = args.get("callbackId").get(); + async_out["callbackId"] = picojson::value(callback_id); + this->bluetooth_gatt_server_.SetWriteValueRequestCallback(args, async_out); + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + void BluetoothInstance::BluetoothGATTServerSendResponse(const picojson::value& args, picojson::object& out) { ScopeLogger(); diff --git a/src/bluetooth/bluetooth_instance.h b/src/bluetooth/bluetooth_instance.h index 9dabeafb..a7043b2e 100644 --- a/src/bluetooth/bluetooth_instance.h +++ b/src/bluetooth/bluetooth_instance.h @@ -126,6 +126,8 @@ class BluetoothInstance : public common::ParsedInstance { void BluetoothGATTServerUnregisterService(const picojson::value& args, picojson::object& out); void BluetoothGATTServerSetReadValueRequestCallback(const picojson::value& args, picojson::object& out); + void BluetoothGATTServerSetWriteValueRequestCallback(const picojson::value& args, + picojson::object& out); void BluetoothGATTServerSendResponse(const picojson::value& args, picojson::object& out); BluetoothAdapter bluetooth_adapter_;