// Copyright 2022 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 "sharing/nearby_connections_service_impl.h" #include #include #include #include #include #include #include #include "absl/container/flat_hash_map.h" #include "absl/strings/string_view.h" #include "absl/time/time.h" #include "absl/types/span.h" #include "connections/listeners.h" #include "connections/medium_selector.h" #include "connections/strategy.h" #include "internal/analytics/event_logger.h" #include "internal/platform/logging.h" #include "internal/platform/mac_address.h" #include "sharing/internal/public/connectivity_manager.h" #include "sharing/nearby_connections_service.h" #include "sharing/nearby_connections_types.h" namespace nearby { namespace sharing { namespace { Core* GetService(NearbyConnectionsService::HANDLE handle) { return reinterpret_cast(handle); } } // namespace NearbyConnectionsServiceImpl::NearbyConnectionsServiceImpl( nearby::ConnectivityManager* connectivity_manager, nearby::analytics::EventLogger* event_logger) : connectivity_manager_(*connectivity_manager) { static ServiceControllerRouter* router = new ServiceControllerRouter([this]() { // WARNING: there can be only 1 instance of // NearbyConnectionsServiceImpl, otherwise the router could be pointing // at an invalid instance. return connectivity_manager_.IsHPRealtekDevice(); }); static Core* core = new Core(event_logger, router); service_handle_ = core; } NearbyConnectionsServiceImpl::~NearbyConnectionsServiceImpl() = default; void NearbyConnectionsServiceImpl::StartAdvertising( absl::string_view service_id, const std::vector& endpoint_info, const AdvertisingOptions& advertising_options, ConnectionListener advertising_listener, std::function callback) { advertising_listener_ = std::move(advertising_listener); NcAdvertisingOptions options{}; options.strategy = ConvertToServiceStrategy(advertising_options.strategy); options.allowed.ble = advertising_options.allowed_mediums.ble; options.allowed.bluetooth = advertising_options.allowed_mediums.bluetooth; options.allowed.web_rtc = advertising_options.allowed_mediums.web_rtc; options.allowed.wifi_lan = advertising_options.allowed_mediums.wifi_lan; options.auto_upgrade_bandwidth = advertising_options.auto_upgrade_bandwidth; options.enforce_topology_constraints = advertising_options.enforce_topology_constraints; options.enable_bluetooth_listening = advertising_options.enable_bluetooth_listening; options.enable_webrtc_listening = advertising_options.enable_webrtc_listening; options.use_stable_endpoint_id = advertising_options.use_stable_endpoint_id; options.force_new_endpoint_id = advertising_options.force_new_endpoint_id; options.fast_advertisement_service_uuid = advertising_options.fast_advertisement_service_uuid.uuid; NcConnectionRequestInfo connection_request_info; connection_request_info.endpoint_info = NcByteArray(std::string(endpoint_info.begin(), endpoint_info.end())); connection_request_info.listener.initiated_cb = [&](const std::string& endpoint_id, const NcConnectionResponseInfo& info) { ConnectionInfo connection_info; connection_info.authentication_token = info.authentication_token; std::string remote_end_point = std::string(info.remote_endpoint_info); connection_info.endpoint_info = std::vector( remote_end_point.begin(), remote_end_point.end()); connection_info.is_incoming_connection = info.is_incoming_connection; std::string raw_authentication_token = std::string(info.raw_authentication_token); connection_info.raw_authentication_token = std::vector( raw_authentication_token.begin(), raw_authentication_token.end()); advertising_listener_.initiated_cb(endpoint_id, connection_info); }; connection_request_info.listener.accepted_cb = [&](const std::string& endpoint_id) { advertising_listener_.accepted_cb(endpoint_id); }; connection_request_info.listener.rejected_cb = [&](const std::string& endpoint_info, NcStatus status) { advertising_listener_.rejected_cb(endpoint_info, ConvertToStatus(status)); }; connection_request_info.listener.disconnected_cb = [&](const std::string& endpoint_info) { advertising_listener_.disconnected_cb(endpoint_info); }; connection_request_info.listener.bandwidth_changed_cb = [&](const std::string& endpoint_id, NcMedium medium) { advertising_listener_.bandwidth_changed_cb(endpoint_id, static_cast(medium)); }; GetService(service_handle_) ->StartAdvertising(service_id, options, std::move(connection_request_info), BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::StopAdvertising( absl::string_view service_id, std::function callback) { GetService(service_handle_)->StopAdvertising(BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::StartDiscovery( absl::string_view service_id, const DiscoveryOptions& discovery_options, DiscoveryListener discovery_listener, std::function callback) { discovery_listener_ = std::move(discovery_listener); NcDiscoveryOptions options{}; options.strategy = ConvertToServiceStrategy(discovery_options.strategy); // NcDiscoveryOptions enabled all mediums by default, we should apply the // settings from discovery_options. options.allowed.SetAll(false); options.allowed.ble = discovery_options.allowed_mediums.ble; options.allowed.bluetooth = discovery_options.allowed_mediums.bluetooth; options.allowed.web_rtc = discovery_options.allowed_mediums.web_rtc; options.allowed.wifi_lan = discovery_options.allowed_mediums.wifi_lan; if (discovery_options.fast_advertisement_service_uuid.has_value()) { options.fast_advertisement_service_uuid = (*discovery_options.fast_advertisement_service_uuid).uuid; } options.is_out_of_band_connection = discovery_options.is_out_of_band_connection; if (discovery_options.alternate_service_uuid.has_value()) { options.ble_options.alternate_uuid = discovery_options.alternate_service_uuid; } NcDiscoveryListener listener; listener.endpoint_found_cb = [this](const std::string& endpoint_id, const NcByteArray& endpoint_info, const std::string& service_id) { std::string endpoint_info_data = std::string(endpoint_info); discovery_listener_.endpoint_found_cb( endpoint_id, DiscoveredEndpointInfo(std::vector(endpoint_info_data.begin(), endpoint_info_data.end()), service_id)); }; listener.endpoint_lost_cb = [this](const std::string& endpoint_id) { discovery_listener_.endpoint_lost_cb(endpoint_id); }; listener.endpoint_distance_changed_cb = [this](const std::string& endpoint_id, NcDistanceInfo distance_info) { discovery_listener_.endpoint_distance_changed_cb( endpoint_id, static_cast(distance_info)); }; GetService(service_handle_) ->StartDiscovery(service_id, options, std::move(listener), BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::StopDiscovery( absl::string_view service_id, std::function callback) { GetService(service_handle_)->StopDiscovery(BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::RequestConnection( absl::string_view service_id, const std::vector& endpoint_info, absl::string_view endpoint_id, const ConnectionOptions& connection_options, ConnectionListener connection_listener, std::function callback) { connection_listener_ = std::move(connection_listener); NcConnectionOptions options{}; options.allowed.ble = connection_options.allowed_mediums.ble; options.allowed.bluetooth = connection_options.allowed_mediums.bluetooth; options.allowed.web_rtc = connection_options.allowed_mediums.web_rtc; options.allowed.wifi_lan = connection_options.allowed_mediums.wifi_lan; options.allowed.wifi_hotspot = connection_options.allowed_mediums.wifi_hotspot; if (connection_options.keep_alive_interval.has_value()) { options.keep_alive_interval_millis = *connection_options.keep_alive_interval / absl::Milliseconds(1); } if (connection_options.keep_alive_timeout.has_value()) { options.keep_alive_timeout_millis = *connection_options.keep_alive_timeout / absl::Milliseconds(1); } if (connection_options.remote_bluetooth_mac_address.has_value()) { MacAddress mac_address; MacAddress::FromBytes( absl::MakeConstSpan(*connection_options.remote_bluetooth_mac_address), mac_address); options.remote_bluetooth_mac_address = mac_address; } options.non_disruptive_hotspot_mode = connection_options.non_disruptive_hotspot_mode; NcConnectionRequestInfo connection_request_info; connection_request_info.endpoint_info = NcByteArray(std::string(endpoint_info.begin(), endpoint_info.end())); connection_request_info.listener.initiated_cb = [&](const std::string& endpoint_id, const NcConnectionResponseInfo& info) { ConnectionInfo connection_info; connection_info.authentication_token = info.authentication_token; std::string remote_end_point = std::string(info.remote_endpoint_info); connection_info.endpoint_info = std::vector( remote_end_point.begin(), remote_end_point.end()); connection_info.is_incoming_connection = info.is_incoming_connection; std::string raw_authentication_token = std::string(info.raw_authentication_token); connection_info.raw_authentication_token = std::vector( raw_authentication_token.begin(), raw_authentication_token.end()); connection_listener_.initiated_cb(endpoint_id, connection_info); }; connection_request_info.listener.accepted_cb = [&](const std::string& endpoint_id) { connection_listener_.accepted_cb(endpoint_id); }; connection_request_info.listener.rejected_cb = [&](const std::string& endpoint_info, NcStatus status) { connection_listener_.rejected_cb(endpoint_info, ConvertToStatus(status)); }; connection_request_info.listener.disconnected_cb = [&](const std::string& endpoint_info) { connection_listener_.disconnected_cb(endpoint_info); }; connection_request_info.listener.bandwidth_changed_cb = [&](const std::string& endpoint_id, NcMedium medium) { connection_listener_.bandwidth_changed_cb(endpoint_id, static_cast(medium)); }; GetService(service_handle_) ->RequestConnection(endpoint_id, std::move(connection_request_info), options, BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::DisconnectFromEndpoint( absl::string_view service_id, absl::string_view endpoint_id, std::function callback) { GetService(service_handle_) ->DisconnectFromEndpoint(endpoint_id, BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::SendPayload( absl::string_view service_id, absl::Span endpoint_ids, std::unique_ptr payload, std::function callback) { GetService(service_handle_) ->SendPayload(endpoint_ids, ConvertToServicePayload(*payload), BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::CancelPayload( absl::string_view service_id, int64_t payload_id, std::function callback) { GetService(service_handle_) ->CancelPayload(payload_id, BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::InitiateBandwidthUpgrade( absl::string_view service_id, absl::string_view endpoint_id, std::function callback) { GetService(service_handle_) ->InitiateBandwidthUpgrade(endpoint_id, BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::AcceptConnection( absl::string_view service_id, absl::string_view endpoint_id, PayloadListener payload_listener, std::function callback) { payload_listeners_.emplace(endpoint_id, std::move(payload_listener)); NcPayloadListener service_payload_listener{ .payload_cb = [&](absl::string_view endpoint_id, NcPayload payload) { auto payload_listener = payload_listeners_.find(endpoint_id); if (payload_listener == payload_listeners_.end()) { return; } VLOG(1) << "payload callback id=" << payload.GetId(); switch (payload.GetType()) { case NcPayloadType::kBytes: case NcPayloadType::kFile: payload_listener->second.payload_cb( endpoint_id, ConvertToPayload(std::move(payload))); break; default: // TODO(b/219814719); support stream payload. break; } }, .payload_progress_cb = [&](absl::string_view endpoint_id, const NcPayloadProgressInfo& info) { PayloadTransferUpdate transfer_update; transfer_update.bytes_transferred = info.bytes_transferred; transfer_update.payload_id = info.payload_id; transfer_update.status = static_cast(info.status); transfer_update.total_bytes = info.total_bytes; VLOG(1) << "payload transfer update id=" << info.payload_id; auto payload_listener = payload_listeners_.find(endpoint_id); if (payload_listener != payload_listeners_.end()) { payload_listener->second.payload_progress_cb(endpoint_id, transfer_update); } }}; GetService(service_handle_) ->AcceptConnection(endpoint_id, std::move(service_payload_listener), BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::StopAllEndpoints( std::function callback) { GetService(service_handle_)->StopAllEndpoints(BuildResultCallback(callback)); } void NearbyConnectionsServiceImpl::SetCustomSavePath( absl::string_view path, std::function callback) { GetService(service_handle_) ->SetCustomSavePath(path, BuildResultCallback(callback)); } std::string NearbyConnectionsServiceImpl::Dump() const { return GetService(service_handle_)->Dump(); } } // namespace sharing } // namespace nearby