From: Pawel Wasowski Date: Tue, 18 Aug 2020 15:17:01 +0000 (+0200) Subject: [Bluetooth] Implement BluetoothGATTServerCharacteristic::setReadValueRequestCallback X-Git-Tag: submit/tizen/20200918.124009~9 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=10f6001aa4e220bbfc5773c8cf677bf40b3111e4;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Bluetooth] Implement BluetoothGATTServerCharacteristic::setReadValueRequestCallback [Verification] Basic use cases were tested in Chrome Dev Tools and run properly. More thorough verification should yet be done. Change-Id: I4d59ac356889c132fc0901435a2fd0d7e7c5c9df Signed-off-by: Pawel Wasowski Signed-off-by: Arkadiusz Pietraszek --- diff --git a/src/bluetooth/bluetooth_api.js b/src/bluetooth/bluetooth_api.js index b2410921..98059558 100755 --- a/src/bluetooth/bluetooth_api.js +++ b/src/bluetooth/bluetooth_api.js @@ -2470,6 +2470,155 @@ Object.defineProperty(BluetoothGATTServerCharacteristic.prototype, 'constructor' writable: true }); +tizen.GATTRequestReply = function(statusCode, data) { + AV.isConstructorCall(this, tizen.GATTRequestReply); + + var statusCode_ = Converter.toLong(statusCode); + var data_ = T.isNullOrUndefined(data) ? null : BluetoothManager_toByteArray(data); + + Object.defineProperties(this, { + statusCode: { + enumerable: true, + get: function() { + return statusCode_; + }, + set: function(v) { + statusCode_ = Converter.toLong(v); + } + }, + data: { + enumerable: true, + get: function() { + return data_; + }, + set: function(v) { + data_ = T.isNullOrUndefined(v) ? null : numberArrayToByteArray(v); + } + } + }); +}; + +function _createReadValueRequestCallback( + _id, + sendResponseSuccessCallback, + sendResponseErrorCallback +) { + return _singleListenerBuilder( + 'ReadValueRequestCallback_' + _id, + /* + * _singleListenerBuilder requires 2 callbacks, the second of which + * is an error callback. + * Read value request events coming from the native layer + * are never errors. + * Hence, we don't use the second callback here. + */ + function(event, readValueRequestCallback, unusedErrorCallback) { + var clientAddress = event.clientAddress; + var offset = event.offset; + privUtils_.warn('event: ' + JSON.stringify(event)); + + if (readValueRequestCallback) { + var requestReply = readValueRequestCallback(clientAddress, offset); + var response = { + _id: _id, + requestId: event.requestId, + requestType: event.requestType, + offset: offset, + statusCode: requestReply.statusCode, + data: requestReply.data + }; + privUtils_.warn('event: ' + JSON.stringify(response)); + 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 = {}; + +BluetoothGATTServerCharacteristic.prototype.setReadValueRequestCallback = function() { + var args = AV.validateArgs(arguments, [ + { + name: 'readValueRequestCallback', + 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 characteristicId = this._id; + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + var readValueRequestCallback = _createReadValueRequestCallback( + characteristicId, + args.sendResponseSuccessCallback, + args.sendResponseErrorCallback + ); + readValueRequestCallback.addListener( + args.readValueRequestCallback, + function unusedErrorCallback() { + /* Intentionally no operation */ + } + ); + _BluetoothGATTServerReadWriteValueRequestCallbacks[ + "ReadValueCallback" + characteristicId + ] = readValueRequestCallback; + native.callIfPossible(args.successCallback, native.getErrorObject(result)); + } + }; + + var callArgs = { _id: this._id }; + var result = native.call( + 'BluetoothGATTServerSetReadValueRequestCallback', + callArgs, + callback + ); + + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } +}; + /** * Creates a manager for specified listener event. Manager handles multiple * registered listeners @@ -3805,7 +3954,7 @@ BluetoothGATTServer.prototype.getConnectionMtu = function() { throw native.getErrorObjectAndValidate(result, BluetoothGATTServer_valid_getConnectionMtu_exceptions, UnknownError); } -} +}; var GATTServer = new BluetoothGATTServer(); diff --git a/src/bluetooth/bluetooth_gatt_client_service.cc b/src/bluetooth/bluetooth_gatt_client_service.cc index 1db6db3b..fd7a5319 100644 --- a/src/bluetooth/bluetooth_gatt_client_service.cc +++ b/src/bluetooth/bluetooth_gatt_client_service.cc @@ -59,7 +59,8 @@ bool IsProperty(int propertyBits, bt_gatt_property_e property) { } } -BluetoothGATTClientService::BluetoothGATTClientService(BluetoothInstance& instance) : instance_(instance) { +BluetoothGATTClientService::BluetoothGATTClientService(BluetoothInstance& instance) + : instance_(instance) { ScopeLogger(); } @@ -118,8 +119,8 @@ void BluetoothGATTClientService::TryDestroyClient(const std::string& address) { } PlatformResult BluetoothGATTClientService::GetSpecifiedGATTClient(const std::string& address, - const UUID& uuid, - picojson::object* result) { + const UUID& uuid, + picojson::object* result) { ScopeLogger(); bt_gatt_client_h client = GetGattClient(address); @@ -180,8 +181,9 @@ void BluetoothGATTClientService::GetServices(const picojson::value& args, picojs } } -PlatformResult BluetoothGATTClientService::GetServicesHelper(bt_gatt_h handle, const std::string& address, - picojson::array* array) { +PlatformResult BluetoothGATTClientService::GetServicesHelper(bt_gatt_h handle, + const std::string& address, + picojson::array* array) { ScopeLogger(); if (!IsStillConnected(address)) { @@ -228,7 +230,8 @@ PlatformResult BluetoothGATTClientService::GetServicesHelper(bt_gatt_h handle, c return PlatformResult(ErrorCode::NO_ERROR); } -void BluetoothGATTClientService::GetCharacteristics(const picojson::value& args, picojson::object& out) { +void BluetoothGATTClientService::GetCharacteristics(const picojson::value& args, + picojson::object& out) { ScopeLogger(); bt_gatt_h handle = (bt_gatt_h) static_cast(args.get("handle").get()); @@ -245,8 +248,8 @@ void BluetoothGATTClientService::GetCharacteristics(const picojson::value& args, } PlatformResult BluetoothGATTClientService::GetCharacteristicsHelper(bt_gatt_h handle, - const std::string& address, - picojson::array* array) { + const std::string& address, + picojson::array* array) { ScopeLogger(); if (!IsStillConnected(address)) { @@ -518,7 +521,7 @@ void BluetoothGATTClientService::WriteValue(const picojson::value& args, picojso } void BluetoothGATTClientService::AddValueChangeListener(const picojson::value& args, - picojson::object& out) { + picojson::object& out) { ScopeLogger(); const auto& address = args.get("address").get(); if (!IsStillConnected(address)) { @@ -542,7 +545,7 @@ void BluetoothGATTClientService::AddValueChangeListener(const picojson::value& a } void BluetoothGATTClientService::RemoveValueChangeListener(const picojson::value& args, - picojson::object& out) { + picojson::object& out) { ScopeLogger(); const auto& address = args.get("address").get(); if (!IsStillConnected(address)) { @@ -565,7 +568,7 @@ void BluetoothGATTClientService::RemoveValueChangeListener(const picojson::value } common::PlatformResult BluetoothGATTClientService::GetServiceAllUuids(const std::string& address, - picojson::array* array) { + picojson::array* array) { ScopeLogger(); bt_gatt_client_h client = GetGattClient(address); @@ -610,7 +613,7 @@ common::PlatformResult BluetoothGATTClientService::GetServiceAllUuids(const std: } void BluetoothGATTClientService::OnCharacteristicValueChanged(bt_gatt_h characteristic, char* value, - int length, void* user_data) { + int length, void* user_data) { ScopeLogger("characteristic: [%p], len: [%d], user_data: [%p]", characteristic, length, user_data); diff --git a/src/bluetooth/bluetooth_gatt_client_service.h b/src/bluetooth/bluetooth_gatt_client_service.h index 2d727eae..9f405658 100644 --- a/src/bluetooth/bluetooth_gatt_client_service.h +++ b/src/bluetooth/bluetooth_gatt_client_service.h @@ -36,7 +36,7 @@ class BluetoothGATTClientService { ~BluetoothGATTClientService(); common::PlatformResult GetSpecifiedGATTClient(const std::string& address, const UUID& uuid, - picojson::object* result); + picojson::object* result); void TryDestroyClient(const std::string& address); void GetServices(const picojson::value& data, picojson::object& out); diff --git a/src/bluetooth/bluetooth_gatt_server.cc b/src/bluetooth/bluetooth_gatt_server.cc index d33c2755..b0fd2e43 100644 --- a/src/bluetooth/bluetooth_gatt_server.cc +++ b/src/bluetooth/bluetooth_gatt_server.cc @@ -191,6 +191,33 @@ void BluetoothGATTServer::UnregisterService(const picojson::value& args, picojso ReportSuccess(out); } +void BluetoothGATTServer::SetReadValueRequestCallback(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + auto result = service_.SetReadValueRequestCallback(args); + + if (result.IsError()) { + ReportError(result, &out); + return; + } + + ReportSuccess(out); +} + +void BluetoothGATTServer::SendResponse(const picojson::value& args, picojson::object& out) { + ScopeLogger(); + + auto result = service_.SendResponse(args); + + if (result.IsError()) { + ReportError(result, &out); + return; + } + + ReportSuccess(out); +} + bool BluetoothGATTServer::IsRunning() { return running_; } diff --git a/src/bluetooth/bluetooth_gatt_server.h b/src/bluetooth/bluetooth_gatt_server.h index 525d921b..b3cd1507 100644 --- a/src/bluetooth/bluetooth_gatt_server.h +++ b/src/bluetooth/bluetooth_gatt_server.h @@ -40,6 +40,8 @@ class BluetoothGATTServer { void GetConnectionMtu(const picojson::value& args, picojson::object& out); 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 SendResponse(const picojson::value& args, picojson::object& out); bool IsRunning(); static bool DestroyService(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 85688215..8d0bc02a 100644 --- a/src/bluetooth/bluetooth_gatt_server_service.cc +++ b/src/bluetooth/bluetooth_gatt_server_service.cc @@ -18,6 +18,7 @@ #include "bluetooth/bluetooth_gatt_server.h" #include "bluetooth/bluetooth_gatt_server_service.h" +#include "bluetooth/bluetooth_instance.h" #include "bluetooth/bluetooth_util.h" #include "bluetooth/uuid.h" @@ -57,6 +58,14 @@ const std::string kEncryptedSignedWritePermission = "encryptedSignedWritePermiss const std::string kId = "_id"; const std::string kIdsToRemoveFromNativeLayer = "idsToRemoveFromNativeLayer"; +const std::string kClientAddress = "clientAddress"; +const std::string kRequestId = "requestId"; +const std::string kOffset = "offset"; +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"; int GetPermissionsInt(const picojson::value& permissions) { ScopeLogger("permissions: %s", permissions.serialize().c_str()); @@ -164,7 +173,9 @@ bool BluetoothGATTServerService::DestroyDescriptor(int total, int index, bt_gatt return true; } -BluetoothGATTServerService::BluetoothGATTServerService() { + +BluetoothGATTServerService::BluetoothGATTServerService(BluetoothInstance& instance) + : instance_{instance}, rw_request_callback_data_{instance, callback_names_} { ScopeLogger(); } @@ -410,13 +421,102 @@ common::PlatformResult BluetoothGATTServerService::UnregisterService(const picoj auto ids_to_remove = args.get(kIdsToRemoveFromNativeLayer).get(); for (const auto& to_remove : ids_to_remove) { - int id_to_remove = static_cast(to_remove.get()); + const auto id_to_remove = static_cast(to_remove.get()); + + 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); gatt_objects_.erase(id_to_remove); } return common::PlatformResult{}; } +PlatformResult BluetoothGATTServerService::SetReadValueRequestCallback( + const picojson::value& args) { + ScopeLogger(); + + auto _id = static_cast(args.get(kId).get()); + 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); + + auto read_value_request_callback = [](const char* remote_address, int request_id, + bt_gatt_server_h server, bt_gatt_h gatt_handle, int offset, + void* user_data) -> void { + ScopeLogger("ReadValueRequestCallback called. remote_address: %s, request_id: %d, offset: %d", + remote_address, request_id, offset); + auto rw_callback_data = static_cast(user_data); + + rw_callback_data->instance_.GetWorker().add_job( + [remote_address, request_id, server, gatt_handle, offset, rw_callback_data] { + ScopeLogger("Async call: SetReadValueRequestCallback"); + + auto read_value_request = picojson::value{picojson::object{}}; + auto& read_value_request_obj = read_value_request.get(); + read_value_request_obj[kClientAddress] = picojson::value{remote_address}; + 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]; + 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); + }); + }; + + auto ret = bt_gatt_server_set_read_value_requested_cb(gatt_handle, read_value_request_callback, + &rw_request_callback_data_); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_gatt_server_set_read_value_requested_cb() failed: %d (%s)", ret, + get_error_message(ret)); + return BluetoothErrorToPlatformResult(ret, "Failed to set read value request callback"); + } + + LoggerD("bt_gatt_server_set_read_value_requested_cb(): SUCCESS"); + return common::PlatformResult{}; +} + +PlatformResult BluetoothGATTServerService::SendResponse(const picojson::value& args) { + ScopeLogger(); + + 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(); + auto request_type = (request_type_str == kReadRequestType) ? BT_GATT_REQUEST_TYPE_READ + : BT_GATT_REQUEST_TYPE_WRITE; + auto offset = static_cast(args.get(kOffset).get()); + auto status_code = static_cast(args.get(kStatusCode).get()); + + std::unique_ptr value{nullptr}; + int value_size = 0; + if (args.get(kData).is()) { + auto value_octets = args.get(kData).get(); + value_size = value_octets.size(); + value = std::make_unique(value_size); + for (int i = 0; i < value_size; ++i) { + value[i] += static_cast(value_octets[i].get()); + } + } else { + LoggerD("No data!"); + } + + LoggerD( + "Sending response: _id: %d, requestId: %d, requestType: %s, offset: %d, statusCode: %d, " + "value: [%s], 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, + value.get(), value_size); + if (BT_ERROR_NONE != ret) { + LoggerE("bt_gatt_server_send_response() failed: %d (%s)", ret, get_error_message(ret)); + return BluetoothErrorToPlatformResult(ret, "Failed to send response"); + } + LoggerD("bt_gatt_server_send_response(): SUCCESS"); + + 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 cecd9d2b..e53916b3 100644 --- a/src/bluetooth/bluetooth_gatt_server_service.h +++ b/src/bluetooth/bluetooth_gatt_server_service.h @@ -18,6 +18,7 @@ #define BLUETOOTH_BLUETOOTH_GATT_SERVER_SERVICE_H_ #include +#include #include #include @@ -34,7 +35,7 @@ class BluetoothInstance; class BluetoothGATTServerService { public: - BluetoothGATTServerService(); + BluetoothGATTServerService(BluetoothInstance& instance); ~BluetoothGATTServerService(); PlatformResult CreateService(const picojson::value& args, bt_gatt_h* new_service_handle, @@ -42,9 +43,19 @@ class BluetoothGATTServerService { PlatformResult RegisterService(bt_gatt_h service_handle, bt_gatt_server_h server, const std::vector>& new_gatt_objects); PlatformResult UnregisterService(const picojson::value& args, bt_gatt_server_h server); + PlatformResult SetReadValueRequestCallback(const picojson::value& args); + PlatformResult SendResponse(const picojson::value& args); private: std::map gatt_objects_; + std::map callback_names_; + + BluetoothInstance& instance_; + + struct ReadWriteRequestCallbackData { + BluetoothInstance& instance_; + std::map& callback_names_map_; + } rw_request_callback_data_; 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_instance.cc b/src/bluetooth/bluetooth_instance.cc index 2c3fc5a6..1366972c 100644 --- a/src/bluetooth/bluetooth_instance.cc +++ b/src/bluetooth/bluetooth_instance.cc @@ -37,7 +37,7 @@ BluetoothInstance::BluetoothInstance() bluetooth_le_adapter_(*this), bluetooth_gatt_client_service_(*this), bluetooth_le_device_(*this, bluetooth_gatt_client_service_), - bluetooth_gatt_server_service_(), + bluetooth_gatt_server_service_(*this), worker(), bluetooth_gatt_server_(*this, bluetooth_gatt_server_service_) { ScopeLogger(); @@ -112,6 +112,8 @@ BluetoothInstance::BluetoothInstance() REGISTER_METHOD(BluetoothGATTServerGetConnectionMtu); REGISTER_METHOD(BluetoothGATTServerRegisterService); REGISTER_METHOD(BluetoothGATTServerUnregisterService); + REGISTER_METHOD(BluetoothGATTServerSetReadValueRequestCallback); + REGISTER_METHOD(BluetoothGATTServerSendResponse); #undef REGISTER_METHOD } @@ -183,6 +185,10 @@ void BluetoothInstance::FireEvent(const std::string& event, FireEvent(event, *value.get()); } +common::Worker& BluetoothInstance::GetWorker() { + return worker; +} + void BluetoothInstance::BluetoothAdapterSetName(const picojson::value& args, picojson::object& out) { ScopeLogger(); @@ -581,5 +587,41 @@ void BluetoothInstance::BluetoothGATTServerUnregisterService(const picojson::val ReportSuccess(out); } +void BluetoothInstance::BluetoothGATTServerSetReadValueRequestCallback(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out); + + worker.add_job([this, args] { + ScopeLogger("Async call: BluetoothGATTServerSetReadValueRequestCallback"); + 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_.SetReadValueRequestCallback(args, async_out); + this->PostMessage(response.serialize().c_str()); + }); + + ReportSuccess(out); +} + +void BluetoothInstance::BluetoothGATTServerSendResponse(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + CHECK_PRIVILEGE_ACCESS(kPrivilegeBluetooth, &out); + + worker.add_job([this, args] { + ScopeLogger("Async call: BluetoothGATTServerSendSyncResponse"); + 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_.SendResponse(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 0f5eeb04..9dabeafb 100644 --- a/src/bluetooth/bluetooth_instance.h +++ b/src/bluetooth/bluetooth_instance.h @@ -50,6 +50,8 @@ class BluetoothInstance : public common::ParsedInstance { void FireEvent(const std::string& event, const picojson::value& value); void FireEvent(const std::string& event, const std::shared_ptr& value); + common::Worker& GetWorker(); + private: void BluetoothAdapterSetName(const picojson::value& args, picojson::object& out); void BluetoothAdapterSetPowered(const picojson::value& args, picojson::object& out); @@ -122,6 +124,9 @@ class BluetoothInstance : public common::ParsedInstance { void BluetoothGATTServerGetConnectionMtu(const picojson::value& args, picojson::object& out); void BluetoothGATTServerRegisterService(const picojson::value& args, picojson::object& out); void BluetoothGATTServerUnregisterService(const picojson::value& args, picojson::object& out); + void BluetoothGATTServerSetReadValueRequestCallback(const picojson::value& args, + picojson::object& out); + void BluetoothGATTServerSendResponse(const picojson::value& args, picojson::object& out); BluetoothAdapter bluetooth_adapter_; BluetoothDevice bluetooth_device_; diff --git a/src/bluetooth/bluetooth_le_device.cc b/src/bluetooth/bluetooth_le_device.cc index fc2da2f8..dbe1465d 100644 --- a/src/bluetooth/bluetooth_le_device.cc +++ b/src/bluetooth/bluetooth_le_device.cc @@ -60,7 +60,8 @@ const char* kMtu = "mtu"; const char* kAddress = "address"; } -BluetoothLEDevice::BluetoothLEDevice(BluetoothInstance& instance, BluetoothGATTClientService& service) +BluetoothLEDevice::BluetoothLEDevice(BluetoothInstance& instance, + BluetoothGATTClientService& service) : instance_(instance), service_(service), is_listener_set_(false) { ScopeLogger(); int ret = bt_gatt_set_connection_state_changed_cb(GattConnectionState, this);