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