diff --git a/connections/c/BUILD b/connections/c/BUILD index 17db7c4f..80a5f822 100644 --- a/connections/c/BUILD +++ b/connections/c/BUILD @@ -111,6 +111,49 @@ lexan.cc_windows_dll( ], ) +cc_library( + name = "nc_types", + hdrs = [ + "nc.h", + "nc_def.h", + "nc_types.h", + ], + compatible_with = ["//buildenv/target:non_prod"], +) + +cc_library( + name = "nc", + srcs = [ + "nc.cc", + ], + compatible_with = ["//buildenv/target:non_prod"], + copts = ["-DNC_DLL"], + deps = [ + ":nc_types", + "//connections:core", + "//connections:core_types", + "//internal/platform:base", + "//internal/platform:types", + "//proto:connections_enums_cc_proto", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", + ], +) + +lexan.cc_windows_dll( + name = "nc_windows", + srcs = [], + compatible_with = ["//buildenv/target:non_prod"], + copts = ["-DNC_DLL"], + tags = ["windows-dll"], + deps = [ + ":nc", + "//internal/platform/implementation/windows", + "@com_google_absl//absl/strings", + ], +) + cc_test( name = "connections_test", size = "small", diff --git a/connections/c/nc.cc b/connections/c/nc.cc new file mode 100644 index 00000000..f01fbbce --- /dev/null +++ b/connections/c/nc.cc @@ -0,0 +1,572 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "connections/c/nc.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" +#include "absl/strings/string_view.h" +#include "absl/types/span.h" +#include "connections/advertising_options.h" +#include "connections/c/nc_types.h" +#include "connections/connection_options.h" +#include "connections/core.h" +#include "connections/discovery_options.h" +#include "connections/listeners.h" +#include "connections/medium_selector.h" +#include "connections/out_of_band_connection_metadata.h" +#include "connections/params.h" +#include "connections/payload.h" +#include "connections/status.h" +#include "connections/strategy.h" +#include "internal/platform/bluetooth_utils.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/file.h" +#include "internal/platform/logging.h" + +namespace nearby::connections { +class Core; +class ServiceController; +class ServiceControllerRouter; +class OfflineServiceController; +} // namespace nearby::connections + +typedef struct NcContext { + nearby::connections::ServiceControllerRouter* router = nullptr; + nearby::connections::Core* core = nullptr; +} NcContext; + +static NcContext kNcContext; + +int64_t getFileSize(const char* filename) { + struct stat file_status; + if (stat(filename, &file_status) < 0) { + return -1; + } + + return file_status.st_size; +} + +nearby::connections::ConnectionRequestInfo GetCppConnectionRequestInfo( + const NC_CONNECTION_REQUEST_INFO& connection_request_info) { + nearby::connections::ConnectionRequestInfo cpp_connection_request_info; + cpp_connection_request_info.endpoint_info = + nearby::ByteArray(connection_request_info.endpoint_info.data, + connection_request_info.endpoint_info.size); + nearby::connections::ConnectionListener cpp_connection_listener; + cpp_connection_listener.accepted_cb = [=](const std::string& endpoint_id) { + connection_request_info.accepted_callback(endpoint_id.c_str()); + }; + cpp_connection_listener.bandwidth_changed_cb = + [=](const std::string& endpoint_id, nearby::connections::Medium medium) { + connection_request_info.bandwidth_changed_callback( + endpoint_id.c_str(), static_cast(medium)); + }; + cpp_connection_listener.disconnected_cb = + [=](const std::string& endpoint_id) { + connection_request_info.disconnected_callback(endpoint_id.c_str()); + }; + cpp_connection_listener.initiated_cb = + [=](const std::string& endpoint_id, + const nearby::connections::ConnectionResponseInfo& info) { + NC_CONNECTION_RESPONSE_INFO connection_response_info; + connection_response_info.is_connection_verified = + info.is_connection_verified; + connection_response_info.is_incoming_connection = + info.is_incoming_connection; + connection_response_info.remote_endpoint_info.data = + (char*)info.remote_endpoint_info.data(); + connection_response_info.remote_endpoint_info.size = + info.remote_endpoint_info.size(); + connection_response_info.authentication_token.data = + (char*)info.authentication_token.data(); + connection_response_info.authentication_token.size = + info.authentication_token.size(); + connection_response_info.raw_authentication_token.data = + (char*)info.raw_authentication_token.data(); + connection_response_info.raw_authentication_token.size = + info.raw_authentication_token.size(); + + connection_request_info.initiated_callback(endpoint_id.c_str(), + connection_response_info); + }; + + cpp_connection_listener.rejected_cb = + [=](const std::string& endpoint_id, nearby::connections::Status status) { + connection_request_info.rejected_callback( + endpoint_id.c_str(), static_cast(status.value)); + }; + + cpp_connection_request_info.listener = std::move(cpp_connection_listener); + return cpp_connection_request_info; +} + +NC_INSTANCE NcOpenService() { + if (kNcContext.core == nullptr) { + kNcContext.router = new nearby::connections::ServiceControllerRouter(); + kNcContext.core = new nearby::connections::Core(kNcContext.router); + } + + return kNcContext.core; +} + +void NcCloseService(NC_INSTANCE instance) { + if (kNcContext.core == nullptr) { + return; + } + + kNcContext.core->StopAllEndpoints([](nearby::connections::Status status) { + NEARBY_LOGS(INFO) << "Stopping all endpoints with status " + << status.ToString(); + }); + delete kNcContext.router; + delete kNcContext.core; + kNcContext.router = nullptr; + kNcContext.core = nullptr; +} + +void NcStartAdvertising( + NC_INSTANCE instance, const char* service_id, + const NC_ADVERTISING_OPTIONS& advertising_options, + const NC_CONNECTION_REQUEST_INFO& connection_request_info, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + nearby::connections::ConnectionRequestInfo cpp_connection_request_info = + GetCppConnectionRequestInfo(connection_request_info); + + nearby::connections::AdvertisingOptions cpp_advertising_options; + cpp_advertising_options.allowed.ble = + advertising_options.common_options.allowed_mediums[NC_MEDIUM_BLE]; + cpp_advertising_options.allowed.bluetooth = + advertising_options.common_options.allowed_mediums[NC_MEDIUM_BLUETOOTH]; + cpp_advertising_options.allowed.wifi_lan = + advertising_options.common_options.allowed_mediums[NC_MEDIUM_WIFI_LAN]; + cpp_advertising_options.allowed.wifi_direct = + advertising_options.common_options.allowed_mediums[NC_MEDIUM_WIFI_DIRECT]; + cpp_advertising_options.allowed.wifi_hotspot = + advertising_options.common_options + .allowed_mediums[NC_MEDIUM_WIFI_HOTSPOT]; + cpp_advertising_options.allowed.web_rtc = + advertising_options.common_options.allowed_mediums[NC_MEDIUM_WEB_RTC]; + cpp_advertising_options.enable_bluetooth_listening = + advertising_options.enable_bluetooth_listening; + cpp_advertising_options.enable_webrtc_listening = + advertising_options.enable_webrtc_listening; + cpp_advertising_options.auto_upgrade_bandwidth = + advertising_options.auto_upgrade_bandwidth; + cpp_advertising_options.enforce_topology_constraints = + advertising_options.enforce_topology_constraints; + if (advertising_options.fast_advertisement_service_uuid.size > 0) { + cpp_advertising_options.fast_advertisement_service_uuid = + std::string(advertising_options.fast_advertisement_service_uuid.data, + advertising_options.fast_advertisement_service_uuid.size); + } + + cpp_advertising_options.is_out_of_band_connection = + advertising_options.is_out_of_band_connection; + cpp_advertising_options.low_power = advertising_options.low_power; + + if (advertising_options.common_options.strategy.type == + NC_STRATEGY_TYPE_NONE) { + cpp_advertising_options.strategy = nearby::connections::Strategy::kNone; + } + if (advertising_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_CLUSTER) + cpp_advertising_options.strategy = + nearby::connections::Strategy::kP2pCluster; + if (advertising_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_POINT_TO_POINT) + cpp_advertising_options.strategy = + nearby::connections::Strategy::kP2pPointToPoint; + if (advertising_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_STAR) + cpp_advertising_options.strategy = nearby::connections::Strategy::kP2pStar; + + kNcContext.core->StartAdvertising( + service_id, std::move(cpp_advertising_options), + std::move(cpp_connection_request_info), + [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcStopAdvertising(NC_INSTANCE instance, NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + kNcContext.core->StopAdvertising([=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcStartDiscovery(NC_INSTANCE instance, const char* service_id, + const NC_DISCOVERY_OPTIONS& discovery_options, + const NC_DISCOVERY_LISTENER& discovery_listener, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + nearby::connections::DiscoveryOptions cpp_discovery_options; + + if (discovery_options.common_options.strategy.type == NC_STRATEGY_TYPE_NONE) + cpp_discovery_options.strategy = nearby::connections::Strategy::kNone; + if (discovery_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_CLUSTER) + cpp_discovery_options.strategy = nearby::connections::Strategy::kP2pCluster; + if (discovery_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_POINT_TO_POINT) + cpp_discovery_options.strategy = + nearby::connections::Strategy::kP2pPointToPoint; + if (discovery_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_STAR) + cpp_discovery_options.strategy = nearby::connections::Strategy::kP2pStar; + cpp_discovery_options.auto_upgrade_bandwidth = + discovery_options.auto_upgrade_bandwidth; + cpp_discovery_options.enforce_topology_constraints = + discovery_options.enforce_topology_constraints; + cpp_discovery_options.is_out_of_band_connection = + discovery_options.is_out_of_band_connection; + if (discovery_options.fast_advertisement_service_uuid.size > 0) { + cpp_discovery_options.fast_advertisement_service_uuid = + std::string(discovery_options.fast_advertisement_service_uuid.data, + discovery_options.fast_advertisement_service_uuid.size); + } + cpp_discovery_options.allowed.bluetooth = + discovery_options.common_options.allowed_mediums[NC_MEDIUM_BLUETOOTH]; + cpp_discovery_options.allowed.ble = + discovery_options.common_options.allowed_mediums[NC_MEDIUM_BLE]; + cpp_discovery_options.allowed.wifi_lan = + discovery_options.common_options.allowed_mediums[NC_MEDIUM_WIFI_LAN]; + cpp_discovery_options.allowed.wifi_hotspot = + discovery_options.common_options.allowed_mediums[NC_MEDIUM_WIFI_HOTSPOT]; + cpp_discovery_options.allowed.web_rtc = + discovery_options.common_options.allowed_mediums[NC_MEDIUM_WEB_RTC]; + + nearby::connections::DiscoveryListener listener; + listener.endpoint_distance_changed_cb = + [=](const std::string& endpoint_id, + nearby::connections::DistanceInfo info) { + discovery_listener.endpoint_distance_changed_callback( + endpoint_id.c_str(), static_cast(info)); + }; + listener.endpoint_found_cb = [=](const std::string& endpoint_id, + const nearby::ByteArray& endpoint_info, + const std::string& service_id) { + discovery_listener.endpoint_found_callback( + endpoint_id.c_str(), + NC_DATA{.size = endpoint_info.size(), + .data = (char*)endpoint_info.data()}, + service_id.c_str()); + }; + + listener.endpoint_lost_cb = [=](const std::string& endpoint_id) { + discovery_listener.endpoint_lost_callback(endpoint_id.c_str()); + }; + + kNcContext.core->StartDiscovery( + service_id, std::move(cpp_discovery_options), std::move(listener), + [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcStopDiscovery(NC_INSTANCE instance, NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + kNcContext.core->StopDiscovery([=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcInjectEndpoint(NC_INSTANCE instance, const char* service_id, + const NC_OUT_OF_BAND_CONNECTION_METADATA& metadata, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + nearby::connections::OutOfBandConnectionMetadata + cpp_out_of_band_connection_metadata; + cpp_out_of_band_connection_metadata.endpoint_id = metadata.endpoint_id; + cpp_out_of_band_connection_metadata.endpoint_info = { + metadata.endpoint_info.data, metadata.endpoint_info.size}; + cpp_out_of_band_connection_metadata.medium = + static_cast(metadata.medium); + cpp_out_of_band_connection_metadata.remote_bluetooth_mac_address = { + metadata.remote_bluetooth_mac_address.data, + metadata.remote_bluetooth_mac_address.size}; + + kNcContext.core->InjectEndpoint( + service_id, cpp_out_of_band_connection_metadata, + [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcRequestConnection( + NC_INSTANCE instance, const char* endpoint_id, + const NC_CONNECTION_REQUEST_INFO& connection_request_info, + const NC_CONNECTION_OPTIONS& connection_options, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + nearby::connections::ConnectionRequestInfo cpp_connection_request_info = + GetCppConnectionRequestInfo(connection_request_info); + + nearby::connections::ConnectionOptions cpp_connection_options; + cpp_connection_options.allowed.ble = + connection_options.common_options.allowed_mediums[NC_MEDIUM_BLE]; + cpp_connection_options.allowed.bluetooth = + connection_options.common_options.allowed_mediums[NC_MEDIUM_BLUETOOTH]; + cpp_connection_options.allowed.web_rtc = + connection_options.common_options.allowed_mediums[NC_MEDIUM_WEB_RTC]; + cpp_connection_options.allowed.wifi_lan = + connection_options.common_options.allowed_mediums[NC_MEDIUM_WIFI_LAN]; + cpp_connection_options.auto_upgrade_bandwidth = + connection_options.auto_upgrade_bandwidth; + cpp_connection_options.enforce_topology_constraints = + connection_options.enforce_topology_constraints; + if (connection_options.fast_advertisement_service_uuid.size > 0) { + cpp_connection_options.fast_advertisement_service_uuid = + std::string(connection_options.fast_advertisement_service_uuid.data, + connection_options.fast_advertisement_service_uuid.size); + } + cpp_connection_options.is_out_of_band_connection = + connection_options.is_out_of_band_connection; + cpp_connection_options.keep_alive_interval_millis = + connection_options.keep_alive_interval_millis; + cpp_connection_options.keep_alive_timeout_millis = + connection_options.keep_alive_timeout_millis; + cpp_connection_options.low_power = connection_options.low_power; + if (connection_options.remote_bluetooth_mac_address.size > 0) { + cpp_connection_options.remote_bluetooth_mac_address = + nearby::BluetoothUtils::FromString( + std::string(connection_options.remote_bluetooth_mac_address.data, + connection_options.remote_bluetooth_mac_address.size)); + } + if (connection_options.common_options.strategy.type == NC_STRATEGY_TYPE_NONE) + cpp_connection_options.strategy = nearby::connections::Strategy::kNone; + if (connection_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_CLUSTER) + cpp_connection_options.strategy = + nearby::connections::Strategy::kP2pCluster; + if (connection_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_POINT_TO_POINT) + cpp_connection_options.strategy = + nearby::connections::Strategy::kP2pPointToPoint; + if (connection_options.common_options.strategy.type == + NC_STRATEGY_TYPE_P2P_STAR) + cpp_connection_options.strategy = nearby::connections::Strategy::kP2pStar; + + kNcContext.core->RequestConnection( + endpoint_id, std::move(cpp_connection_request_info), + std::move(cpp_connection_options), + [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcAcceptConnection(NC_INSTANCE instance, const char* endpoint_id, + NC_PAYLOAD_LISTENER payload_listener, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + nearby::connections::PayloadListener cpp_payload_listener; + cpp_payload_listener.payload_cb = [=](absl::string_view endpoint_id, + nearby::connections::Payload payload) { + NC_PAYLOAD nc_payload; + nc_payload.id = payload.GetId(); + nc_payload.direction = NC_PAYLOAD_DIRECTION_INCOMING; + nc_payload.type = static_cast(payload.GetType()); + if (nc_payload.type == NC_PAYLOAD_TYPE_BYTES) { + nearby::ByteArray bytes = payload.AsBytes(); + nc_payload.content.bytes.content.data = bytes.data(); + nc_payload.content.bytes.content.size = bytes.size(); + } else if (nc_payload.type == NC_PAYLOAD_TYPE_FILE) { + nc_payload.content.file.file_name = (char*)payload.GetFileName().c_str(); + nc_payload.content.file.parent_folder = + (char*)payload.GetParentFolder().c_str(); + } else if (nc_payload.type == NC_PAYLOAD_TYPE_STREAM) { + // TODO(guogang): support stream later. + } + + payload_listener.received_callback(std::string(endpoint_id).c_str(), + nc_payload); + }; + + cpp_payload_listener.payload_progress_cb = + [=](absl::string_view endpoint_id, + const nearby::connections::PayloadProgressInfo& progress) { + NC_PAYLOAD_PROGRESS_INFO nc_payload_progress_info; + nc_payload_progress_info.id = progress.payload_id; + nc_payload_progress_info.bytes_transferred = progress.bytes_transferred; + nc_payload_progress_info.total_bytes = progress.total_bytes; + nc_payload_progress_info.status = + static_cast(progress.status); + payload_listener.progress_updated_callback( + std::string(endpoint_id).c_str(), nc_payload_progress_info); + }; + + kNcContext.core->AcceptConnection( + endpoint_id, std::move(cpp_payload_listener), + [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcRejectConnection(NC_INSTANCE instance, const char* endpoint_id, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + kNcContext.core->RejectConnection( + endpoint_id, [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcSendPayload(NC_INSTANCE instance, size_t endpoint_ids_size, + const char** endpoint_ids, const NC_PAYLOAD& payload, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + std::vector endpoint_ids_vector; + for (size_t i = 0; i < endpoint_ids_size; ++i) { + endpoint_ids_vector.push_back(std::string(endpoint_ids[i])); + } + + absl::Span endpoint_ids_span(endpoint_ids_vector.data(), + endpoint_ids_size); + nearby::connections::Payload cpp_payload; + if (payload.type == NC_PAYLOAD_TYPE_BYTES) { + cpp_payload = nearby::connections::Payload( + payload.id, nearby::ByteArray(payload.content.bytes.content.data, + payload.content.bytes.content.size)); + } else if (payload.type == NC_PAYLOAD_TYPE_FILE) { + // get file size + std::string full_file_name = ""; + if (payload.content.file.parent_folder == nullptr) { + full_file_name = payload.content.file.file_name; + } else { + full_file_name = absl::StrCat(payload.content.file.parent_folder, "/", + payload.content.file.file_name); + } + + nearby::InputFile input_file(full_file_name, + getFileSize(full_file_name.c_str())); + cpp_payload = + nearby::connections::Payload(payload.id, std::move(input_file)); + } else if (payload.type == NC_PAYLOAD_TYPE_STREAM) { + // TODO(guogang): support stream later. + } + + kNcContext.core->SendPayload( + endpoint_ids_span, std::move(cpp_payload), + [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcCancelPayload(NC_INSTANCE instance, NC_PAYLOAD_ID payload_id, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + kNcContext.core->CancelPayload( + payload_id, [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcDisconnectFromEndpoint(NC_INSTANCE instance, const char* endpoint_id, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + kNcContext.core->DisconnectFromEndpoint( + endpoint_id, [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcStopAllEndpoints(NC_INSTANCE instance, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + kNcContext.core->StopAllEndpoints([=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +void NcInitiateBandwidthUpgrade(NC_INSTANCE instance, const char* endpoint_id, + NcCallbackResult result_callback) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + result_callback(NC_STATUS_ERROR); + return; + } + + kNcContext.core->InitiateBandwidthUpgrade( + endpoint_id, [=](nearby::connections::Status status) { + result_callback(static_cast(status.value)); + }); +} + +char* NcGetLocalEndpointId(NC_INSTANCE instance) { + if (kNcContext.core == nullptr || instance != kNcContext.core) { + return nullptr; + } + + std::string endpoint_id = kNcContext.core->GetLocalEndpointId(); + char* result = new char[endpoint_id.length() + 1]; + absl::SNPrintF(result, endpoint_id.length() + 1, "%s", endpoint_id); + return result; +} diff --git a/connections/c/nc.h b/connections/c/nc.h new file mode 100644 index 00000000..e85d5113 --- /dev/null +++ b/connections/c/nc.h @@ -0,0 +1,172 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_H_ + +#include + +#include "connections/c/nc_def.h" +#include "connections/c/nc_types.h" + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// Opens Nearby Connections service. If service is opened already, it will +// return opened instance. +NC_API NC_INSTANCE NcOpenService(); +NC_API void NcCloseService(NC_INSTANCE instance); + +// Starts adververtising an endpoint using Nearby Connections. +// +// instance - The returned instance by NcOpenService. +// service_id - The ID for the service to be advertised. +// advertising_options - The options for advertising. +// connection_request_info - Connection parameters, including endpoint info +// and listeners. +// result_callback - The result of the API operation. +NC_API void NcStartAdvertising( + NC_INSTANCE instance, const char* service_id, + const NC_ADVERTISING_OPTIONS& advertising_options, + const NC_CONNECTION_REQUEST_INFO& connection_request_info, + NcCallbackResult result_callback); + +// Stops advertising a local endpoint. It should be called after calling +// StartAdvertising. +// +// instance - The instance is used to start advertising. +// result_callback - The result of the API operation. +NC_API void NcStopAdvertising(NC_INSTANCE instance, + NcCallbackResult result_callback); + +// Starts to discover for remote endpoints with the specified service ID. +// +// instance - The returned instance by NcOpenService. +// service_id - The ID for the service to be discovered. +// discovery_options - The options for discovery. +// discovery_listener - The callbacks notified when a remote endpoint is +// reported. +// result_callback - The result of the API operation. +NC_API void NcStartDiscovery(NC_INSTANCE instance, const char* service_id, + const NC_DISCOVERY_OPTIONS& discovery_options, + const NC_DISCOVERY_LISTENER& discovery_listener, + NcCallbackResult result_callback); + +// Stops discovering for a running discovery. +// +// instance - The instance is used to start discovery. +// result_callback - The result of the API operation. +NC_API void NcStopDiscovery(NC_INSTANCE instance, + NcCallbackResult result_callback); + +// Invokes the discovery callback from a previous call to NcStartDiscovery() +// with the given endpoint info. The previous call to NcStartDiscovery() must +// have been passed ConnectionOptions with is_out_of_band_connection == true. +// +// instance - The instance is used to start discovery. +// service_id - The ID for the service to be discovered, as specified in the +// corresponding call to NcStartDiscovery(). +// metadata - Metadata used in order to inject the endpoint. +// result_callback - The result of the API operation. +NC_API void NcInjectEndpoint(NC_INSTANCE instance, const char* service_id, + const NC_OUT_OF_BAND_CONNECTION_METADATA& metadata, + NcCallbackResult result_callback); + +// Sends a request to connect to a remote endpoint. +// +// instance - The returned instance by NcOpenService. +// endpoint_id - The identifier for the remote endpoint to which a +// connection request will be sent. +// connection_request_info - Connection parameters. +// connection_options - Options to connect. +// result_callback - The result of the API operation. +NC_API void NcRequestConnection( + NC_INSTANCE instance, const char* endpoint_id, + const NC_CONNECTION_REQUEST_INFO& connection_request_info, + const NC_CONNECTION_OPTIONS& connection_options, + NcCallbackResult result_callback); + +// Accepts a connection to a remote endpoint. +// +// instance - The returned instance by NcOpenService. +// endpoint_id - The identifier for the remote endpoint. +// payload_listener - A callback for payloads exchanged with the remote +// endpoint. +// result_callback - The result of the API operation. +NC_API void NcAcceptConnection(NC_INSTANCE instance, const char* endpoint_id, + NC_PAYLOAD_LISTENER payload_listener, + NcCallbackResult result_callback); + +// Rejects a connection from a remote endpoint. +// +// instance - The returned instance by NcOpenService. +// endpoint_id - The identifier for the remote endpoint. +// result_callback - The result of the API operation. +NC_API void NcRejectConnection(NC_INSTANCE instance, const char* endpoint_id, + NcCallbackResult result_callback); + +// Sends a Payload to a remote endpoint. +// +// instance - The returned instance by NcOpenService. +// endpoint_ids_size - The endpoint number to receive the payload. +// endpoint_ids - The endpoint ID array. +// payload - the payload will be sent. +// result_callback - The result of the API operation. +NC_API void NcSendPayload(NC_INSTANCE instance, size_t endpoint_ids_size, + const char** endpoint_ids, const NC_PAYLOAD& payload, + NcCallbackResult result_callback); + +// Cancels a Payload currently in-flight to or from remote endpoint(s). +// +// instance - The Nearby Connections instance is called by NcSendPayload. +// payload_id - The payload ID of payload to cancel. +// result_callback - The result of the API operation. +NC_API void NcCancelPayload(NC_INSTANCE instance, NC_PAYLOAD_ID payload_id, + NcCallbackResult result_callback); + +// Disconnects from a remote endpoint. +// +// instance - The returned instance by NcOpenService. +// endpoint_id - The endpoint ID of remote device to disconnect. +// result_callback - The result of the API operation. +NC_API void NcDisconnectFromEndpoint(NC_INSTANCE instance, + const char* endpoint_id, + NcCallbackResult result_callback); + +// Disconnects from, and removes all traces of, all connected and/or +// discovered endpoints. +// +// instance - The returned instance by NcOpenService. +// result_callback - The result of the API operation. +NC_API void NcStopAllEndpoints(NC_INSTANCE instance, + NcCallbackResult result_callback); + +// Sends a request to initiate connection bandwidth upgrade. +// +// instance - The returned instance by NcOpenService. +// endpoint_id - Requested to upgrade on the remote device with the endpoint ID. +// result_callback - The result of the API operation. +NC_API void NcInitiateBandwidthUpgrade(NC_INSTANCE instance, + const char* endpoint_id, + NcCallbackResult result_callback); + +// Gets the local endpoint generated by Nearby Connections. +NC_API char* NcGetLocalEndpointId(NC_INSTANCE instance); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_H_ diff --git a/connections/c/nc_def.h b/connections/c/nc_def.h new file mode 100644 index 00000000..7af2172a --- /dev/null +++ b/connections/c/nc_def.h @@ -0,0 +1,39 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_DEF_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_DEF_H_ + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +#ifdef _WIN32 // These storage class specifiers only matter to win32 dll + // builds. +#ifdef NC_DLL +// If we're building the core, we're exporting. +#define NC_API __declspec(dllexport) +#else // !NC_DLL +// If we're not building the core, we're importing. +#define NC_API __declspec(dllimport) +#endif // NC_DLL +#else // !_WIN32 +#define NC_API // We're not building a win32 dll, leave the source unchanged. +#endif // _WIN32 + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_DEF_H_ diff --git a/connections/c/nc_types.h b/connections/c/nc_types.h new file mode 100644 index 00000000..bcbd7f95 --- /dev/null +++ b/connections/c/nc_types.h @@ -0,0 +1,281 @@ +// Copyright 2024 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_TYPES_H_ +#define THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_TYPES_H_ + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +typedef void* NC_INSTANCE; + +typedef int64_t NC_PAYLOAD_ID; + +// NC_DATA is used to define a byte array. Its last byte is not zero. +typedef struct NC_DATA { + size_t size; + char* data; +} NC_DATA, *PNC_DATA; + +// Supported mediums by Nearby Connections. +typedef enum NC_MEDIUM { + NC_MEDIUM_UNKNOWN = 0, + NC_MEDIUM_MDNS = 1, // deprecated + NC_MEDIUM_BLUETOOTH = 2, + NC_MEDIUM_WIFI_HOTSPOT = 3, + NC_MEDIUM_BLE = 4, + NC_MEDIUM_WIFI_LAN = 5, + NC_MEDIUM_WIFI_AWARE = 6, + NC_MEDIUM_NFC = 7, + NC_MEDIUM_WIFI_DIRECT = 8, + NC_MEDIUM_WEB_RTC = 9, + NC_MEDIUM_BLE_L2CAP = 10, + NC_MEDIUM_USB = 11, + NC_MEDIUM_MAX = 12 +} NC_MEDIUM; + +typedef enum NC_CONNECTION_TYPE { + NC_CONNECTION_TYPE_NONE = 0, + NC_CONNECTION_TYPE_POINT_TO_POINT = 1, +} NC_CONNECTION_TYPE; + +typedef enum NC_TOPOLOGY_TYPE { + NC_TOPOLOGY_TYPE_UNKNOWN = 0, + NC_TOPOLOGY_TYPE_ONE_TO_ONE = 1, + NC_TOPOLOGY_TYPE_ONE_TO_MANY = 2, + NC_TOPOLOGY_TYPE_MANY_TO_MANY = 3, +} NC_TOPOLOGY_TYPE; + +typedef enum NC_STRATEGY_TYPE { + NC_STRATEGY_TYPE_NONE, + NC_STRATEGY_TYPE_P2P_CLUSTER, + NC_STRATEGY_TYPE_P2P_STAR, + NC_STRATEGY_TYPE_P2P_POINT_TO_POINT +} NC_STRATEGY_TYPE; + +typedef enum NC_DISTANCE_INFO { + NC_DISTANCE_INFO_UNKNOWN = 1, + NC_DISTANCE_INFO_VERYCLOSE = 2, + NC_DISTANCE_INFO_CLOSE = 3, + NC_DISTANCE_INFO_FAR = 4, +} NC_DISTANCE_INFO; + +typedef enum NC_STATUS { + NC_STATUS_SUCCESS, + NC_STATUS_ERROR, + NC_STATUS_OUTOFORDERAPICALL, + NC_STATUS_ALREADYHAVEACTIVESTRATEGY, + NC_STATUS_ALREADYADVERTISING, + NC_STATUS_ALREADYDISCOVERING, + NC_STATUS_ALREADYLISTENING, + NC_STATUS_ENDPOINTIOERROR, + NC_STATUS_ENDPOINTUNKNOWN, + NC_STATUS_CONNECTIONREJECTED, + NC_STATUS_ALREADYCONNECTEDTOENDPOINT, + NC_STATUS_NOTCONNECTEDTOENDPOINT, + NC_STATUS_BLUETOOTHERROR, + NC_STATUS_BLEERROR, + NC_STATUS_WIFILANERROR, + NC_STATUS_PAYLOADUNKNOWN, + NC_STATUS_RESET, + NC_STATUS_TIMEOUT, + NC_STATUS_UNKNOWN, + NC_STATUS_NEXTVALUE, +} NC_STATUS; + +typedef enum NC_PAYLOAD_TYPE { + NC_PAYLOAD_TYPE_UNKNOWN = 0, + NC_PAYLOAD_TYPE_BYTES = 1, + NC_PAYLOAD_TYPE_STREAM = 2, + NC_PAYLOAD_TYPE_FILE = 3 +} NC_PAYLOAD_TYPE; + +typedef enum NC_PAYLOAD_DIRECTION { + NC_PAYLOAD_DIRECTION_UNKNOWN = 0, + NC_PAYLOAD_DIRECTION_INCOMING = 1, + NC_PAYLOAD_DIRECTION_OUTGOING = 2, +} NC_PAYLOAD_DIRECTION; + +// Defines struct types in Nearby connections. + +typedef struct NC_STRATEGY { + NC_STRATEGY_TYPE type; + NC_CONNECTION_TYPE connection_type; + NC_TOPOLOGY_TYPE topology_type; +} NC_STRATEGY; + +typedef struct NC_COMMON_OPTIONS { + NC_STRATEGY strategy; + bool allowed_mediums[NC_MEDIUM_MAX]; +} NC_COMMON_OPTIONS; + +typedef struct NC_ADVERTISING_OPTIONS { + NC_COMMON_OPTIONS common_options; + bool auto_upgrade_bandwidth; + bool enforce_topology_constraints; + bool enable_bluetooth_listening; + bool enable_webrtc_listening; + bool low_power; + bool is_out_of_band_connection; + NC_DATA fast_advertisement_service_uuid; + NC_DATA device_info; +} NC_ADVERTISING_OPTIONS, *PNC_ADVERTISING_OPTIONS; + +typedef struct NC_CONNECTION_OPTIONS { + NC_COMMON_OPTIONS common_options; + bool auto_upgrade_bandwidth = true; + bool enforce_topology_constraints; + bool low_power; + bool is_out_of_band_connection = false; + NC_DATA remote_bluetooth_mac_address; + NC_DATA fast_advertisement_service_uuid; + int keep_alive_interval_millis = 0; + int keep_alive_timeout_millis = 0; +} NC_CONNECTION_OPTIONS, *PNC_CONNECTION_OPTIONS; + +typedef struct NC_DISCOVERY_OPTIONS { + NC_COMMON_OPTIONS common_options; + bool auto_upgrade_bandwidth = true; + bool enforce_topology_constraints; + + // Whether this is intended to be used in conjunction with InjectEndpoint(). + bool is_out_of_band_connection = false; + NC_DATA fast_advertisement_service_uuid; +} NC_DISCOVERY_OPTIONS, *PNC_DISCOVERY_OPTIONS; + +typedef struct NC_CONNECTION_RESPONSE_INFO { + NC_DATA remote_endpoint_info; + NC_DATA authentication_token; + NC_DATA raw_authentication_token; + bool is_incoming_connection = false; + bool is_connection_verified = false; +} NC_CONNECTION_RESPONSE_INFO, *PNC_CONNECTION_RESPONSE_INFO; + +// Defines callbacks in Nearby Connections. + +typedef void (*NcCallbackResult)(NC_STATUS status); + +typedef void (*NcCallbackConnectionInitiated)( + const char* endpoint_id, const NC_CONNECTION_RESPONSE_INFO& info); +typedef void (*NcCallbackConnectionAccepted)(const char* endpoint_id); +typedef void (*NcCallbackConnectionRejected)(const char* endpoint_id, + NC_STATUS status); +typedef void (*NcCallbackConnectionDisconnected)(const char* endpoint_id); +typedef void (*NcCallbackConnectionBandwidthChanged)(const char* endpoint_id, + NC_MEDIUM medium); + +typedef struct NC_CONNECTION_REQUEST_INFO { + NC_DATA endpoint_info; + NcCallbackConnectionInitiated initiated_callback; + NcCallbackConnectionAccepted accepted_callback; + NcCallbackConnectionRejected rejected_callback; + NcCallbackConnectionDisconnected disconnected_callback; + NcCallbackConnectionBandwidthChanged bandwidth_changed_callback; +} NC_CONNECTION_REQUEST_INFO; + +typedef void (*NcCallbackDiscoveryEndpointFound)(const char* endpoint_id, + const NC_DATA& endpoint_info, + const char* service_id); +typedef void (*NcCallbackDiscoveryEndpointLost)(const char* endpoint_id); +typedef void (*NcCallbackDiscoveryEndpointDistanceChanged)( + const char* endpoint_id, NC_DISTANCE_INFO info); + +typedef struct NC_DISCOVERY_LISTENER { + NcCallbackDiscoveryEndpointFound endpoint_found_callback; + NcCallbackDiscoveryEndpointLost endpoint_lost_callback; + NcCallbackDiscoveryEndpointDistanceChanged endpoint_distance_changed_callback; +} NC_DISCOVERY_LISTENER; + +typedef struct NC_BYTES_PAYLOAD { + NC_DATA content; +} NC_BYTES_PAYLOAD; + +typedef struct NC_STREAM_PAYLOAD { + NC_DATA data; +} NC_STREAM_PAYLOAD; + +typedef struct NC_FILE_PAYLOAD { + char* file_name; + char* parent_folder; +} NC_FILE_PAYLOAD; + +typedef union NC_PAYLOAD_CONTENT { + NC_BYTES_PAYLOAD bytes; + NC_STREAM_PAYLOAD stream; + NC_FILE_PAYLOAD file; +} NC_PAYLOAD_CONTENT; + +typedef struct NC_PAYLOAD { + NC_PAYLOAD_ID id; + NC_PAYLOAD_TYPE type; + NC_PAYLOAD_DIRECTION direction; + NC_PAYLOAD_CONTENT content; +} NC_PAYLOAD; + +typedef enum NC_PAYLOAD_PROGRESS_INFO_STATUS { + NC_PAYLOAD_PROGRESS_INFO_STATUS_SUCCESS, + NC_PAYLOAD_PROGRESS_INFO_STATUS_FAILURE, + NC_PAYLOAD_PROGRESS_INFO_STATUS_INPROGRESS, + NC_PAYLOAD_PROGRESS_INFO_STATUS_CANCELED, +} NC_PAYLOAD_PROGRESS_INFO_STATUS; + +typedef struct NC_PAYLOAD_PROGRESS_INFO { + NC_PAYLOAD_ID id; + NC_PAYLOAD_PROGRESS_INFO_STATUS status; + size_t total_bytes; + size_t bytes_transferred; +} NC_PAYLOAD_PROGRESS_INFO; + +typedef void (*NcCallbackPayloadReceived)(const char* endpoint_id, + const NC_PAYLOAD& payload); +typedef void (*NcCallbackPayloadProgressUpdated)( + const char* endpoint_id, const NC_PAYLOAD_PROGRESS_INFO& info); + +typedef struct NC_PAYLOAD_LISTENER { + NcCallbackPayloadReceived received_callback; + NcCallbackPayloadProgressUpdated progress_updated_callback; +} NC_PAYLOAD_LISTENER; + +typedef struct NC_OUT_OF_BAND_CONNECTION_METADATA { + // Medium to use for the out-of-band connection. + NC_MEDIUM medium; + + // Endpoint ID to use for the injected connection; will be included in the + // endpoint_found_cb callback. Must be exactly 4 bytes and should be randomly- + // generated such that no two IDs are identical. + char* endpoint_id; + + // Endpoint info to use for the injected connection; will be included in the + // endpoint_found_cb callback. Should uniquely identify the InjectEndpoint() + // call so that the client which made the call can verify the endpoint + // that was found is the one that was injected. + // + // Cannot be empty, and must be <131 bytes. + NC_DATA endpoint_info; + + // Used for Bluetooth connections. + NC_DATA remote_bluetooth_mac_address; +} NC_OUT_OF_BAND_CONNECTION_METADATA, *PNC_OUT_OF_BAND_CONNECTION_METADATA; + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus + +#endif // THIRD_PARTY_NEARBY_CONNECTIONS_C_NC_TYPES_H_