| // Copyright 2020 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/nearby_sharing/nearby_sharing_service_impl.h" |
| |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/task_runner_util.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chrome/browser/nearby_sharing/certificates/nearby_share_certificate_manager_impl.h" |
| #include "chrome/browser/nearby_sharing/client/nearby_share_client_impl.h" |
| #include "chrome/browser/nearby_sharing/common/nearby_share_prefs.h" |
| #include "chrome/browser/nearby_sharing/contacts/nearby_share_contact_manager_impl.h" |
| #include "chrome/browser/nearby_sharing/fast_initiation_manager.h" |
| #include "chrome/browser/nearby_sharing/local_device_data/nearby_share_local_device_data_manager_impl.h" |
| #include "chrome/browser/nearby_sharing/logging/logging.h" |
| #include "chrome/browser/nearby_sharing/nearby_connections_manager.h" |
| #include "chrome/browser/nearby_sharing/transfer_metadata_builder.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/signin/identity_manager_factory.h" |
| #include "chrome/services/sharing/public/cpp/advertisement.h" |
| #include "chrome/services/sharing/public/mojom/nearby_connections_types.mojom.h" |
| #include "components/prefs/pref_service.h" |
| #include "device/bluetooth/bluetooth_adapter_factory.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "ui/base/idle/idle.h" |
| |
| namespace { |
| |
| constexpr base::TimeDelta kReadFramesTimeout = base::TimeDelta::FromSeconds(15); |
| constexpr base::TimeDelta kReadResponseFrameTimeout = |
| base::TimeDelta::FromSeconds(60); |
| |
| std::string ReceiveSurfaceStateToString( |
| NearbySharingService::ReceiveSurfaceState state) { |
| switch (state) { |
| case NearbySharingService::ReceiveSurfaceState::kForeground: |
| return "FOREGROUND"; |
| case NearbySharingService::ReceiveSurfaceState::kBackground: |
| return "BACKGROUND"; |
| case NearbySharingService::ReceiveSurfaceState::kUnknown: |
| return "UNKNOWN"; |
| } |
| } |
| |
| std::string DataUsageToString(DataUsage usage) { |
| switch (usage) { |
| case DataUsage::kOffline: |
| return "OFFLINE"; |
| case DataUsage::kOnline: |
| return "ONLINE"; |
| case DataUsage::kWifiOnly: |
| return "WIFI_ONLY"; |
| case DataUsage::kUnknown: |
| return "UNKNOWN"; |
| } |
| } |
| |
| std::string PowerLevelToString(PowerLevel level) { |
| switch (level) { |
| case PowerLevel::kLowPower: |
| return "LOW_POWER"; |
| case PowerLevel::kMediumPower: |
| return "MEDIUM_POWER"; |
| case PowerLevel::kHighPower: |
| return "HIGH_POWER"; |
| case PowerLevel::kUnknown: |
| return "UNKNOWN"; |
| } |
| } |
| |
| std::string VisibilityToString(Visibility visibility) { |
| switch (visibility) { |
| case Visibility::kNoOne: |
| return "NO_ONE"; |
| case Visibility::kAllContacts: |
| return "ALL_CONTACTS"; |
| case Visibility::kSelectedContacts: |
| return "SELECTED_CONTACTS"; |
| case Visibility::kUnknown: |
| return "UNKNOWN"; |
| } |
| } |
| |
| std::string ConnectionsStatusToString( |
| NearbyConnectionsManager::ConnectionsStatus status) { |
| switch (status) { |
| case NearbyConnectionsManager::ConnectionsStatus::kSuccess: |
| return "SUCCESS"; |
| case NearbyConnectionsManager::ConnectionsStatus::kError: |
| return "ERROR"; |
| case NearbyConnectionsManager::ConnectionsStatus::kOutOfOrderApiCall: |
| return "OUT_OF_ORDER_API_CALL"; |
| case NearbyConnectionsManager::ConnectionsStatus:: |
| kAlreadyHaveActiveStrategy: |
| return "ALREADY_HAVE_ACTIVE_STRATEGY"; |
| case NearbyConnectionsManager::ConnectionsStatus::kAlreadyAdvertising: |
| return "ALREADY_ADVERTISING"; |
| case NearbyConnectionsManager::ConnectionsStatus::kAlreadyDiscovering: |
| return "ALREADY_DISCOVERING"; |
| case NearbyConnectionsManager::ConnectionsStatus::kEndpointIOError: |
| return "ENDPOINT_IO_ERROR"; |
| case NearbyConnectionsManager::ConnectionsStatus::kEndpointUnknown: |
| return "ENDPOINT_UNKNOWN"; |
| case NearbyConnectionsManager::ConnectionsStatus::kConnectionRejected: |
| return "CONNECTION_REJECTED"; |
| case NearbyConnectionsManager::ConnectionsStatus:: |
| kAlreadyConnectedToEndpoint: |
| return "ALREADY_CONNECTED_TO_ENDPOINT"; |
| case NearbyConnectionsManager::ConnectionsStatus::kNotConnectedToEndpoint: |
| return "NOT_CONNECTED_TO_ENDPOINT"; |
| case NearbyConnectionsManager::ConnectionsStatus::kBluetoothError: |
| return "BLUETOOTH_ERROR"; |
| case NearbyConnectionsManager::ConnectionsStatus::kWifiLanError: |
| return "WIFI_LAN_ERROR"; |
| case NearbyConnectionsManager::ConnectionsStatus::kPayloadUnknown: |
| return "PAYLOAD_UNKNOWN"; |
| } |
| } |
| |
| } // namespace |
| |
| NearbySharingServiceImpl::NearbySharingServiceImpl( |
| PrefService* prefs, |
| NotificationDisplayService* notification_display_service, |
| Profile* profile, |
| std::unique_ptr<NearbyConnectionsManager> nearby_connections_manager, |
| NearbyProcessManager* process_manager) |
| : prefs_(prefs), |
| profile_(profile), |
| settings_(prefs), |
| nearby_connections_manager_(std::move(nearby_connections_manager)), |
| process_manager_(process_manager), |
| http_client_factory_(std::make_unique<NearbyShareClientFactoryImpl>( |
| IdentityManagerFactory::GetForProfile(profile), |
| profile->GetURLLoaderFactory(), |
| &nearby_share_http_notifier_)), |
| local_device_data_manager_( |
| NearbyShareLocalDeviceDataManagerImpl::Factory::Create( |
| prefs, |
| http_client_factory_.get())), |
| contact_manager_(NearbyShareContactManagerImpl::Factory::Create()), |
| certificate_manager_( |
| NearbyShareCertificateManagerImpl::Factory::Create()) { |
| DCHECK(prefs_); |
| DCHECK(profile_); |
| DCHECK(nearby_connections_manager_); |
| |
| nearby_process_observer_.Add(process_manager_); |
| |
| if (process_manager_->IsActiveProfile(profile_)) { |
| // TODO(crbug.com/1084576): Initialize NearbyConnectionsManager with |
| // NearbyConnectionsMojom from |process_manager|: |
| // process_manager_->GetOrStartNearbyConnections(profile_) |
| } |
| |
| settings_.AddSettingsObserver(settings_receiver_.BindNewPipeAndPassRemote()); |
| |
| GetBluetoothAdapter(); |
| |
| nearby_notification_manager_ = std::make_unique<NearbyNotificationManager>( |
| notification_display_service, this); |
| } |
| |
| NearbySharingServiceImpl::~NearbySharingServiceImpl() { |
| nearby_notification_manager_.reset(); |
| |
| if (bluetooth_adapter_) |
| bluetooth_adapter_->RemoveObserver(this); |
| } |
| |
| NearbySharingService::StatusCodes NearbySharingServiceImpl::RegisterSendSurface( |
| TransferUpdateCallback* transfer_callback, |
| ShareTargetDiscoveredCallback* discovery_callback, |
| SendSurfaceState state) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| // TODO(crrbug.com/1084644): Implement send surface logic. |
| if (state == SendSurfaceState::kForeground) |
| StartFastInitiationAdvertising(); |
| return StatusCodes::kOk; |
| } |
| |
| NearbySharingService::StatusCodes |
| NearbySharingServiceImpl::UnregisterSendSurface( |
| TransferUpdateCallback* transfer_callback, |
| ShareTargetDiscoveredCallback* discovery_callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| StopFastInitiationAdvertising(); |
| return StatusCodes::kOk; |
| } |
| |
| NearbySharingService::StatusCodes |
| NearbySharingServiceImpl::RegisterReceiveSurface( |
| TransferUpdateCallback* transfer_callback, |
| ReceiveSurfaceState state) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(transfer_callback); |
| DCHECK_NE(state, ReceiveSurfaceState::kUnknown); |
| if (foreground_receive_callbacks_.HasObserver(transfer_callback) || |
| background_receive_callbacks_.HasObserver(transfer_callback)) { |
| NS_LOG(VERBOSE) << __func__ |
| << ": registerReceiveSurface failed. Already registered."; |
| return StatusCodes::kError; |
| } |
| |
| // If the receive surface to be registered is a foreground surface, let it |
| // catch up with most recent transfer metadata immediately. |
| if (state == ReceiveSurfaceState::kForeground && last_incoming_metadata_) { |
| transfer_callback->OnTransferUpdate(last_incoming_metadata_->first, |
| last_incoming_metadata_->second); |
| } |
| |
| if (state == ReceiveSurfaceState::kForeground) { |
| foreground_receive_callbacks_.AddObserver(transfer_callback); |
| } else { |
| background_receive_callbacks_.AddObserver(transfer_callback); |
| } |
| |
| NS_LOG(VERBOSE) << __func__ << ": A ReceiveSurface(" |
| << ReceiveSurfaceStateToString(state) |
| << ") has been registered"; |
| InvalidateReceiveSurfaceState(); |
| return StatusCodes::kOk; |
| } |
| |
| NearbySharingService::StatusCodes |
| NearbySharingServiceImpl::UnregisterReceiveSurface( |
| TransferUpdateCallback* transfer_callback) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(transfer_callback); |
| bool is_foreground = |
| foreground_receive_callbacks_.HasObserver(transfer_callback); |
| bool is_background = |
| background_receive_callbacks_.HasObserver(transfer_callback); |
| if (!is_foreground && !is_background) { |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": unregisterReceiveSurface failed. Unknown TransferUpdateCallback."; |
| return StatusCodes::kError; |
| } |
| |
| if (foreground_receive_callbacks_.might_have_observers() && |
| last_incoming_metadata_ && |
| last_incoming_metadata_->second.is_final_status()) { |
| // We already saw the final status in the foreground. |
| // Nullify it so the next time the user opens sharing, it starts the UI from |
| // the beginning |
| last_incoming_metadata_.reset(); |
| } |
| |
| if (is_foreground) { |
| foreground_receive_callbacks_.RemoveObserver(transfer_callback); |
| } else { |
| background_receive_callbacks_.RemoveObserver(transfer_callback); |
| } |
| |
| // Displays the most recent payload status processed by foreground surfaces on |
| // background surface. |
| if (!foreground_receive_callbacks_.might_have_observers() && |
| last_incoming_metadata_) { |
| for (TransferUpdateCallback& background_callback : |
| background_receive_callbacks_) { |
| background_callback.OnTransferUpdate(last_incoming_metadata_->first, |
| last_incoming_metadata_->second); |
| } |
| } |
| |
| NS_LOG(VERBOSE) << __func__ << ": A ReceiveSurface(" |
| << (is_foreground ? "foreground" : "background") |
| << ") has been unregistered"; |
| |
| InvalidateReceiveSurfaceState(); |
| return StatusCodes::kOk; |
| } |
| |
| void NearbySharingServiceImpl::NearbySharingServiceImpl::SendText( |
| const ShareTarget& share_target, |
| std::string text, |
| StatusCodesCallback status_codes_callback) { |
| std::move(status_codes_callback).Run(StatusCodes::kOk); |
| } |
| |
| void NearbySharingServiceImpl::SendFiles( |
| const ShareTarget& share_target, |
| const std::vector<base::FilePath>& files, |
| StatusCodesCallback status_codes_callback) { |
| std::move(status_codes_callback).Run(StatusCodes::kOk); |
| } |
| |
| void NearbySharingServiceImpl::Accept( |
| const ShareTarget& share_target, |
| StatusCodesCallback status_codes_callback) { |
| std::move(status_codes_callback).Run(StatusCodes::kOk); |
| } |
| |
| void NearbySharingServiceImpl::Reject( |
| const ShareTarget& share_target, |
| StatusCodesCallback status_codes_callback) { |
| std::move(status_codes_callback).Run(StatusCodes::kOk); |
| } |
| |
| void NearbySharingServiceImpl::Cancel( |
| const ShareTarget& share_target, |
| StatusCodesCallback status_codes_callback) { |
| std::move(status_codes_callback).Run(StatusCodes::kOk); |
| } |
| |
| void NearbySharingServiceImpl::Open(const ShareTarget& share_target, |
| StatusCodesCallback status_codes_callback) { |
| std::move(status_codes_callback).Run(StatusCodes::kOk); |
| } |
| |
| NearbyShareSettings* NearbySharingServiceImpl::GetSettings() { |
| return &settings_; |
| } |
| |
| void NearbySharingServiceImpl::OnNearbyProfileChanged(Profile* profile) { |
| // TODO(crbug.com/1084576): Notify UI about the new active profile. |
| } |
| |
| void NearbySharingServiceImpl::OnNearbyProcessStarted() { |
| if (process_manager_->IsActiveProfile(profile_)) |
| NS_LOG(VERBOSE) << __func__ << ": Nearby process started!"; |
| } |
| |
| void NearbySharingServiceImpl::OnNearbyProcessStopped() { |
| if (process_manager_->IsActiveProfile(profile_)) { |
| // TODO(crbug.com/1084576): Check if process should be running and restart |
| // it after a delay. |
| } |
| } |
| |
| void NearbySharingServiceImpl::OnIncomingConnection( |
| const std::string& endpoint_id, |
| const std::vector<uint8_t>& endpoint_info, |
| NearbyConnection* connection) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(connection); |
| // TODO(crbug/1085068): Handle incoming connection; use CertificateManager |
| |
| // TODO(himanshujaju) - Update placeholder implementation |
| ShareTarget share_target; |
| share_target.is_incoming = true; |
| |
| incoming_share_target_info_map_[share_target.id].set_connection(connection); |
| connection->RegisterForDisconnection( |
| base::BindOnce(&NearbySharingServiceImpl::UnregisterShareTarget, |
| weak_ptr_factory_.GetWeakPtr(), share_target)); |
| |
| ReceiveIntroduction(std::move(share_target), /*token=*/base::nullopt); |
| } |
| |
| void NearbySharingServiceImpl::OnEnabledChanged(bool enabled) { |
| if (enabled) { |
| NS_LOG(VERBOSE) << __func__ << ": Nearby sharing enabled!"; |
| } else { |
| NS_LOG(VERBOSE) << __func__ << ": Nearby sharing disabled!"; |
| StopAdvertising(); |
| // TODO(crbug/1085067): Stop discovery. |
| nearby_connections_manager_->Shutdown(); |
| } |
| } |
| |
| void NearbySharingServiceImpl::FlushMojoForTesting() { |
| settings_receiver_.FlushForTesting(); |
| } |
| |
| NearbyShareHttpNotifier* NearbySharingServiceImpl::GetHttpNotifier() { |
| return &nearby_share_http_notifier_; |
| } |
| |
| NearbyShareLocalDeviceDataManager* |
| NearbySharingServiceImpl::GetLocalDeviceDataManager() { |
| return local_device_data_manager_.get(); |
| } |
| |
| NearbyShareContactManager* NearbySharingServiceImpl::GetContactManager() { |
| return contact_manager_.get(); |
| } |
| |
| NearbyShareCertificateManager* |
| NearbySharingServiceImpl::GetCertificateManager() { |
| return certificate_manager_.get(); |
| } |
| |
| bool NearbySharingServiceImpl::IsVisibleInBackground(Visibility visibility) { |
| return visibility == Visibility::kAllContacts || |
| visibility == Visibility::kSelectedContacts; |
| } |
| |
| void NearbySharingServiceImpl::OnVisibilityChanged(Visibility new_visibility) { |
| NS_LOG(VERBOSE) << __func__ << ": Nearby sharing visibility changed to " |
| << VisibilityToString(new_visibility); |
| |
| if (advertising_power_level_ != PowerLevel::kUnknown) { |
| StopAdvertising(); |
| } |
| |
| InvalidateReceiveSurfaceState(); |
| } |
| |
| void NearbySharingServiceImpl::OnDataUsageChanged(DataUsage data_usage) { |
| NS_LOG(VERBOSE) << __func__ << ": Nearby sharing data usage changed to " |
| << DataUsageToString(data_usage); |
| |
| if (advertising_power_level_ != PowerLevel::kUnknown) { |
| StopAdvertising(); |
| } |
| |
| InvalidateReceiveSurfaceState(); |
| } |
| |
| void NearbySharingServiceImpl::OnDeviceNameChanged( |
| const std::string& device_name) { |
| NS_LOG(VERBOSE) << __func__ << ": Nearby sharing device name changed to " |
| << device_name; |
| // TODO(vecore): handle device name change |
| } |
| |
| void NearbySharingServiceImpl::OnAllowedContactsChanged( |
| const std::vector<std::string>& allowed_contacts) { |
| NS_LOG(VERBOSE) << __func__ << ": Nearby sharing visible contacts changed"; |
| // TODO(vecore): handle visible contacts change |
| } |
| |
| void NearbySharingServiceImpl::StartFastInitiationAdvertising() { |
| if (!IsBluetoothPresent() || !IsBluetoothPowered()) { |
| NS_LOG(INFO) << "Failed to advertise FastInitiation. Bluetooth is not " |
| "present or powered."; |
| return; |
| } |
| |
| if (fast_initiation_manager_) { |
| NS_LOG(INFO) << "Failed to advertise FastInitiation. Already advertising."; |
| return; |
| } |
| |
| fast_initiation_manager_ = |
| FastInitiationManager::Factory::Create(bluetooth_adapter_); |
| |
| // TODO(crbug.com/1100686): Determine whether to call StartAdvertising() with |
| // kNotify or kSilent. |
| fast_initiation_manager_->StartAdvertising( |
| FastInitiationManager::FastInitType::kNotify, |
| base::BindOnce( |
| &NearbySharingServiceImpl::OnStartFastInitiationAdvertising, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::BindOnce( |
| &NearbySharingServiceImpl::OnStartFastInitiationAdvertisingError, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void NearbySharingServiceImpl::StopFastInitiationAdvertising() { |
| if (!fast_initiation_manager_) { |
| NS_LOG(INFO) << "Can't stop advertising FastInitiation. Not advertising."; |
| return; |
| } |
| |
| fast_initiation_manager_->StopAdvertising( |
| base::BindOnce(&NearbySharingServiceImpl::OnStopFastInitiationAdvertising, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void NearbySharingServiceImpl::GetBluetoothAdapter() { |
| auto* adapter_factory = device::BluetoothAdapterFactory::Get(); |
| if (!adapter_factory->IsBluetoothSupported()) |
| return; |
| |
| // Because this will be called from the constructor, GetAdapter() may call |
| // OnGetBluetoothAdapter() immediately which can cause problems during tests |
| // since the class is not fully constructed yet. |
| base::SequencedTaskRunnerHandle::Get()->PostTask( |
| FROM_HERE, |
| base::BindOnce( |
| &device::BluetoothAdapterFactory::GetAdapter, |
| base::Unretained(adapter_factory), |
| base::BindOnce(&NearbySharingServiceImpl::OnGetBluetoothAdapter, |
| weak_ptr_factory_.GetWeakPtr()))); |
| } |
| |
| void NearbySharingServiceImpl::OnGetBluetoothAdapter( |
| scoped_refptr<device::BluetoothAdapter> adapter) { |
| bluetooth_adapter_ = adapter; |
| bluetooth_adapter_->AddObserver(this); |
| } |
| |
| void NearbySharingServiceImpl::OnStartFastInitiationAdvertising() { |
| // TODO(hansenmichael): Do not invoke |
| // |register_send_surface_callback_| until Nearby Connections |
| // scanning is kicked off. |
| NS_LOG(VERBOSE) << "Started advertising FastInitiation."; |
| } |
| |
| void NearbySharingServiceImpl::OnStartFastInitiationAdvertisingError() { |
| fast_initiation_manager_.reset(); |
| NS_LOG(ERROR) << "Failed to start FastInitiation advertising."; |
| } |
| |
| void NearbySharingServiceImpl::OnStopFastInitiationAdvertising() { |
| fast_initiation_manager_.reset(); |
| NS_LOG(VERBOSE) << "Stopped advertising FastInitiation"; |
| } |
| |
| bool NearbySharingServiceImpl::IsBluetoothPresent() const { |
| return bluetooth_adapter_.get() && bluetooth_adapter_->IsPresent(); |
| } |
| |
| bool NearbySharingServiceImpl::IsBluetoothPowered() const { |
| return IsBluetoothPresent() && bluetooth_adapter_->IsPowered(); |
| } |
| |
| void NearbySharingServiceImpl::AdapterPresentChanged( |
| device::BluetoothAdapter* adapter, |
| bool present) { |
| if (!present) |
| StopFastInitiationAdvertising(); |
| } |
| |
| void NearbySharingServiceImpl::AdapterPoweredChanged( |
| device::BluetoothAdapter* adapter, |
| bool powered) { |
| if (!powered) |
| StopFastInitiationAdvertising(); |
| } |
| |
| void NearbySharingServiceImpl::InvalidateReceiveSurfaceState() { |
| InvalidateAdvertisingState(); |
| // TODO(crbug/154846208) InvalidateFastInitScan(); |
| } |
| |
| void NearbySharingServiceImpl::InvalidateAdvertisingState() { |
| // Screen is off. Do no work. |
| if (ui::CheckIdleStateIsLocked()) { |
| StopAdvertising(); |
| NS_LOG(VERBOSE) << __func__ |
| << ": Stopping advertising because the screen is locked."; |
| return; |
| } |
| |
| // Check if Wifi or Ethernet LAN is off. Advertisements won't work, so |
| // disable them, unless bluetooth is known to be enabled. Not all platforms |
| // have bluetooth, so wifi LAN is a platform-agnostic check. |
| net::NetworkChangeNotifier::ConnectionType connection_type = |
| net::NetworkChangeNotifier::GetConnectionType(); |
| if (!IsBluetoothPresent() && |
| !(connection_type == |
| net::NetworkChangeNotifier::ConnectionType::CONNECTION_WIFI || |
| connection_type == |
| net::NetworkChangeNotifier::ConnectionType::CONNECTION_ETHERNET)) { |
| StopAdvertising(); |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Stopping advertising because both bluetooth and wifi LAN are " |
| "disabled."; |
| return; |
| } |
| |
| // Nearby Sharing is disabled. Don't advertise. |
| if (!settings_.GetEnabled()) { |
| StopAdvertising(); |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Stopping advertising because Nearby Sharing is disabled."; |
| return; |
| } |
| |
| // We're scanning for other nearby devices. Don't advertise. |
| if (is_scanning_) { |
| StopAdvertising(); |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Stopping advertising because we're scanning for other devices."; |
| return; |
| } |
| |
| if (is_transferring_files_) { |
| StopAdvertising(); |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Stopping advertising because we're currently in the midst of " |
| "a transfer."; |
| return; |
| } |
| |
| if (!foreground_receive_callbacks_.might_have_observers() && |
| !background_receive_callbacks_.might_have_observers()) { |
| StopAdvertising(); |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Stopping advertising because no receive surface is registered."; |
| return; |
| } |
| |
| if (!IsVisibleInBackground(settings_.GetVisibility()) && |
| !foreground_receive_callbacks_.might_have_observers()) { |
| StopAdvertising(); |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Stopping advertising because no high power receive surface " |
| "is registered and device is visible to NO_ONE."; |
| return; |
| } |
| |
| PowerLevel power_level; |
| if (foreground_receive_callbacks_.might_have_observers()) { |
| power_level = PowerLevel::kHighPower; |
| // TODO(crbug/1100367) handle fast init |
| // } else if (isFastInitDeviceNearby) { |
| // power_level = PowerLevel::kMediumPower; |
| } else { |
| power_level = PowerLevel::kLowPower; |
| } |
| |
| DataUsage data_usage = settings_.GetDataUsage(); |
| if (advertising_power_level_ != PowerLevel::kUnknown) { |
| if (power_level == advertising_power_level_) { |
| NS_LOG(VERBOSE) |
| << __func__ |
| << "Failed to advertise because we're already advertising with power" |
| << " level " << PowerLevelToString(advertising_power_level_) |
| << " and data usage preference " |
| << DataUsageToString(settings_.GetDataUsage()); |
| return; |
| } |
| |
| StopAdvertising(); |
| NS_LOG(VERBOSE) << __func__ << ": Restart advertising with power level " |
| << PowerLevelToString(power_level) |
| << " and data usage preference " |
| << DataUsageToString(data_usage); |
| } |
| |
| // Starts advertising through Nearby Connections. Caller is expected to ensure |
| // |listener| remains valid until StopAdvertising is called. |
| |
| // TODO(nmusgrave) fill values from CertificateManager |
| std::vector<uint8_t> salt(sharing::Advertisement::kSaltSize, 0); |
| std::vector<uint8_t> encrypted_metadata_key( |
| sharing::Advertisement::kMetadataEncryptionKeyHashByteSize, 0); |
| |
| // TODO(nmusgrave) fill value from local device data manager |
| base::Optional<std::string> device_name = "todo_device_name"; |
| std::vector<uint8_t> endpoint_info = |
| sharing::Advertisement::NewInstance(std::move(salt), |
| std::move(encrypted_metadata_key), |
| std::move(device_name)) |
| ->ToEndpointInfo(); |
| nearby_connections_manager_->StartAdvertising( |
| std::move(endpoint_info), |
| /* listener= */ this, power_level, data_usage, |
| base::BindOnce([](NearbyConnectionsManager::ConnectionsStatus status) { |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Advertising attempted over Nearby Connections with result " |
| << ConnectionsStatusToString(status); |
| })); |
| |
| advertising_power_level_ = power_level; |
| NS_LOG(VERBOSE) << __func__ |
| << ": Advertising has started over Nearby Connections: " |
| << " power level " << PowerLevelToString(power_level) |
| << " visibility " |
| << VisibilityToString(settings_.GetVisibility()) |
| << " data usage " << DataUsageToString(data_usage); |
| return; |
| } |
| |
| void NearbySharingServiceImpl::StopAdvertising() { |
| if (advertising_power_level_ == PowerLevel::kUnknown) { |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Failed to stop advertising because we weren't advertising"; |
| return; |
| } |
| |
| nearby_connections_manager_->StopAdvertising(); |
| |
| advertising_power_level_ = PowerLevel::kUnknown; |
| NS_LOG(VERBOSE) << __func__ << ": Advertising has stopped"; |
| } |
| |
| void NearbySharingServiceImpl::OnIncomingTransferUpdate( |
| const ShareTarget& share_target, |
| TransferMetadata metadata) { |
| if (metadata.status() != TransferMetadata::Status::kCancelled && |
| metadata.status() != TransferMetadata::Status::kRejected) { |
| last_incoming_metadata_ = std::make_pair(share_target, metadata); |
| } else { |
| last_incoming_metadata_ = base::nullopt; |
| } |
| |
| // TODO(himanshujaju) - Handle is_final_status() |
| |
| base::ObserverList<TransferUpdateCallback>& transfer_callbacks = |
| foreground_receive_callbacks_.might_have_observers() |
| ? foreground_receive_callbacks_ |
| : background_receive_callbacks_; |
| |
| for (TransferUpdateCallback& callback : transfer_callbacks) { |
| callback.OnTransferUpdate(share_target, metadata); |
| } |
| } |
| |
| void NearbySharingServiceImpl::WriteResponse( |
| NearbyConnection& connection, |
| sharing::nearby::ConnectionResponseFrame::Status status) { |
| sharing::nearby::Frame frame; |
| sharing::nearby::V1Frame* v1_frame = frame.mutable_v1(); |
| v1_frame->mutable_connection_response()->set_status(status); |
| |
| std::vector<uint8_t> data(frame.ByteSize()); |
| frame.SerializeToArray(data.data(), frame.ByteSize()); |
| |
| connection.Write(std::move(data), base::DoNothing()); |
| } |
| |
| void NearbySharingServiceImpl::Fail(const ShareTarget& share_target, |
| TransferMetadata::Status status) { |
| NearbyConnection* connection = GetIncomingConnection(share_target); |
| if (!connection) { |
| NS_LOG(WARNING) << __func__ << ": Fail invoked for unknown share target."; |
| return; |
| } |
| |
| // TODO(himanshujaju) - Create alarm and cancel in RegisterForDisconnection(). |
| |
| // Send response to remote device. |
| sharing::nearby::ConnectionResponseFrame::Status response_status; |
| switch (status) { |
| case TransferMetadata::Status::kNotEnoughSpace: |
| response_status = |
| sharing::nearby::ConnectionResponseFrame::NOT_ENOUGH_SPACE; |
| break; |
| |
| case TransferMetadata::Status::kUnsupportedAttachmentType: |
| response_status = |
| sharing::nearby::ConnectionResponseFrame::UNSUPPORTED_ATTACHMENT_TYPE; |
| break; |
| |
| case TransferMetadata::Status::kTimedOut: |
| response_status = sharing::nearby::ConnectionResponseFrame::TIMED_OUT; |
| break; |
| |
| default: |
| response_status = sharing::nearby::ConnectionResponseFrame::UNKNOWN; |
| break; |
| } |
| |
| WriteResponse(*connection, response_status); |
| |
| if (incoming_share_target_info_map_.count(share_target.id)) { |
| OnIncomingTransferUpdate( |
| share_target, TransferMetadataBuilder().set_status(status).build()); |
| } |
| } |
| |
| void NearbySharingServiceImpl::ReceiveIntroduction( |
| ShareTarget share_target, |
| base::Optional<std::string> token) { |
| NS_LOG(INFO) << __func__ << ": Receiving introduction from " |
| << share_target.device_name; |
| |
| NearbyConnection* connection = GetIncomingConnection(share_target); |
| if (!connection) { |
| NS_LOG(WARNING) |
| << __func__ |
| << ": Ignore introduction, due to no connection established."; |
| return; |
| } |
| |
| auto frames_reader = std::make_unique<IncomingFramesReader>( |
| process_manager_, profile_, connection); |
| |
| frames_reader->ReadFrame( |
| sharing::mojom::V1Frame::Tag::INTRODUCTION, |
| base::BindOnce(&NearbySharingServiceImpl::OnReceivedIntroduction, |
| weak_ptr_factory_.GetWeakPtr(), std::move(share_target), |
| std::move(token), std::move(frames_reader)), |
| kReadFramesTimeout); |
| } |
| |
| void NearbySharingServiceImpl::OnReceivedIntroduction( |
| ShareTarget share_target, |
| base::Optional<std::string> token, |
| std::unique_ptr<IncomingFramesReader> frames_reader, |
| base::Optional<sharing::mojom::V1FramePtr> frame) { |
| NearbyConnection* connection = GetIncomingConnection(share_target); |
| if (!connection) { |
| NS_LOG(WARNING) |
| << __func__ |
| << ": Ignore received introduction, due to no connection established."; |
| return; |
| } |
| |
| if (!frame) { |
| connection->Close(); |
| NS_LOG(WARNING) << __func__ << ": Invalid introduction frame"; |
| return; |
| } |
| |
| NS_LOG(INFO) << __func__ << ": Successfully read the introduction frame."; |
| |
| sharing::mojom::IntroductionFramePtr introduction_frame = |
| std::move((*frame)->get_introduction()); |
| for (const auto& file : introduction_frame->file_metadata) { |
| if (file->size <= 0) { |
| Fail(share_target, TransferMetadata::Status::kUnsupportedAttachmentType); |
| NS_LOG(WARNING) |
| << __func__ |
| << ": Ignore introduction, due to invalid attachment size"; |
| return; |
| } |
| |
| NS_LOG(VERBOSE) << __func__ << "Found file attachment " << file->name |
| << " of type " << file->type << " with mimeType " |
| << file->mime_type; |
| FileAttachment attachment(file->name, file->type, file->size, |
| /*file_path=*/base::nullopt, file->mime_type); |
| SetAttachmentPayloadId(attachment, file->payload_id); |
| share_target.file_attachments.push_back(std::move(attachment)); |
| } |
| |
| for (const auto& text : introduction_frame->text_metadata) { |
| if (text->size <= 0) { |
| Fail(share_target, TransferMetadata::Status::kUnsupportedAttachmentType); |
| NS_LOG(WARNING) |
| << __func__ |
| << ": Ignore introduction, due to invalid attachment size"; |
| return; |
| } |
| |
| NS_LOG(VERBOSE) << __func__ << "Found text attachment " << text->text_title |
| << " of type " << text->type; |
| TextAttachment attachment(text->text_title, text->type, text->size); |
| SetAttachmentPayloadId(attachment, text->payload_id); |
| share_target.text_attachments.push_back(std::move(attachment)); |
| } |
| |
| if (!share_target.has_attachments()) { |
| NS_LOG(WARNING) << __func__ |
| << ": No attachment is found for this share target. It can " |
| "be result of unrecognizable attachment type"; |
| Fail(share_target, TransferMetadata::Status::kUnsupportedAttachmentType); |
| |
| NS_LOG(VERBOSE) << __func__ |
| << ": We don't support the attachments sent by the sender. " |
| "We have informed " |
| << share_target.device_name; |
| return; |
| } |
| |
| if (IsOutOfStorage(share_target)) { |
| Fail(share_target, TransferMetadata::Status::kNotEnoughSpace); |
| NS_LOG(WARNING) << __func__ |
| << ": Not enough space on the receiver. We have informed " |
| << share_target.device_name; |
| return; |
| } |
| |
| mutual_acceptance_timeout_alarm_.Reset(base::BindOnce( |
| &NearbySharingServiceImpl::OnIncomingMutualAcceptanceTimeout, |
| weak_ptr_factory_.GetWeakPtr(), share_target)); |
| |
| base::SequencedTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, base::BindOnce(mutual_acceptance_timeout_alarm_.callback()), |
| kReadResponseFrameTimeout); |
| |
| OnIncomingTransferUpdate( |
| share_target, |
| TransferMetadataBuilder() |
| .set_status(TransferMetadata::Status::kAwaitingLocalConfirmation) |
| .build()); |
| |
| if (!incoming_share_target_info_map_.count(share_target.id)) { |
| connection->Close(); |
| NS_LOG(VERBOSE) << __func__ |
| << ": IncomingShareTarget not found, disconnecting " |
| << share_target.device_name; |
| return; |
| } |
| |
| connection->RegisterForDisconnection(base::BindOnce( |
| &NearbySharingServiceImpl::OnIncomingConnectionDisconnected, |
| weak_ptr_factory_.GetWeakPtr(), share_target)); |
| |
| // TODO(himanshujaju) - start reader thread. |
| } |
| |
| void NearbySharingServiceImpl::OnIncomingConnectionDisconnected( |
| const ShareTarget& share_target) { |
| OnIncomingTransferUpdate(share_target, |
| TransferMetadataBuilder() |
| .set_status(TransferMetadata::Status::kFailed) |
| .build()); |
| UnregisterShareTarget(share_target); |
| } |
| |
| void NearbySharingServiceImpl::UnregisterShareTarget( |
| const ShareTarget& share_target) { |
| if (share_target.is_incoming) { |
| incoming_share_target_info_map_.erase(share_target.id); |
| nearby_connections_manager_->ClearIncomingPayloads(); |
| } else { |
| // TODO(crbug.com/1084644) - Clear from outgoing map. |
| } |
| mutual_acceptance_timeout_alarm_.Cancel(); |
| } |
| |
| bool NearbySharingServiceImpl::IsOutOfStorage(const ShareTarget& share_target) { |
| // TODO(himanshujaju) - Check storage space based on file path. |
| return false; |
| } |
| |
| void NearbySharingServiceImpl::OnIncomingMutualAcceptanceTimeout( |
| const ShareTarget& share_target) { |
| DCHECK(share_target.is_incoming); |
| |
| NS_LOG(VERBOSE) |
| << __func__ |
| << ": Incoming mutual acceptance timed out, closing connection for " |
| << share_target.device_name; |
| |
| OnIncomingTransferUpdate(share_target, |
| TransferMetadataBuilder() |
| .set_status(TransferMetadata::Status::kTimedOut) |
| .build()); |
| Fail(share_target, TransferMetadata::Status::kTimedOut); |
| } |
| |
| IncomingShareTargetInfo& NearbySharingServiceImpl::GetIncomingShareTargetInfo( |
| const ShareTarget& share_target) { |
| return incoming_share_target_info_map_[share_target.id]; |
| } |
| |
| NearbyConnection* NearbySharingServiceImpl::GetIncomingConnection( |
| const ShareTarget& share_target) { |
| return GetIncomingShareTargetInfo(share_target).connection(); |
| } |
| |
| OutgoingShareTargetInfo& NearbySharingServiceImpl::GetOutgoingShareTargetInfo( |
| const ShareTarget& share_target) { |
| return outgoing_share_target_info_map_[share_target.id]; |
| } |
| |
| void NearbySharingServiceImpl::ClearOutgoingShareTargetInfoMap() { |
| outgoing_share_target_info_map_.clear(); |
| } |
| |
| void NearbySharingServiceImpl::SetAttachmentPayloadId( |
| const Attachment& attachment, |
| int64_t payload_id) { |
| attachment_info_map_[attachment.id()].payload_id = payload_id; |
| } |