blob: 6f16066caa9e42220b8e0fad0e871d70a6ddeddf [file] [log] [blame]
// 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;
}