| // Copyright 2019 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/sharing/sharing_message_sender.h" |
| |
| #include "base/trace_event/trace_event.h" |
| #include "base/uuid.h" |
| #include "chrome/browser/sharing/sharing_constants.h" |
| #include "chrome/browser/sharing/sharing_fcm_sender.h" |
| #include "chrome/browser/sharing/sharing_metrics.h" |
| #include "chrome/browser/sharing/sharing_utils.h" |
| #include "components/send_tab_to_self/target_device_info.h" |
| #include "components/sync_device_info/local_device_info_provider.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| |
| SharingMessageSender::SharingMessageSender( |
| syncer::LocalDeviceInfoProvider* local_device_info_provider) |
| : local_device_info_provider_(local_device_info_provider) {} |
| |
| SharingMessageSender::~SharingMessageSender() = default; |
| |
| base::OnceClosure SharingMessageSender::SendMessageToDevice( |
| const SharingTargetDeviceInfo& device, |
| base::TimeDelta response_timeout, |
| components_sharing_message::SharingMessage message, |
| DelegateType delegate_type, |
| ResponseCallback callback) { |
| DCHECK(message.payload_case() != |
| components_sharing_message::SharingMessage::kAckMessage); |
| |
| int trace_id = GenerateSharingTraceId(); |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN1( |
| "sharing", "Sharing.SendMessage", TRACE_ID_LOCAL(trace_id), |
| "message_type", |
| SharingMessageTypeToString( |
| SharingPayloadCaseToMessageType(message.payload_case()))); |
| |
| std::string message_guid = base::Uuid::GenerateRandomV4().AsLowercaseString(); |
| components_sharing_message::MessageType message_type = |
| SharingPayloadCaseToMessageType(message.payload_case()); |
| |
| auto [it, inserted] = message_metadata_.insert_or_assign( |
| message_guid, SentMessageMetadata( |
| std::move(callback), base::TimeTicks::Now(), |
| message_type, device.platform(), trace_id, |
| SharingChannelType::kUnknown, device.pulse_interval())); |
| DCHECK(inserted); |
| |
| auto delegate_iter = send_delegates_.find(delegate_type); |
| if (delegate_iter == send_delegates_.end()) { |
| InvokeSendMessageCallback(message_guid, |
| SharingSendMessageResult::kInternalError, |
| /*response=*/nullptr); |
| return base::NullCallback(); |
| } |
| SendMessageDelegate* delegate = delegate_iter->second.get(); |
| DCHECK(delegate); |
| |
| // TODO(crbug.com/40103693): Here we assume the caller gets the |device| from |
| // GetDeviceCandidates, so LocalDeviceInfoProvider is ready. It's better to |
| // queue up the message and wait until LocalDeviceInfoProvider is ready. |
| const syncer::DeviceInfo* local_device_info = |
| local_device_info_provider_->GetLocalDeviceInfo(); |
| if (!local_device_info) { |
| InvokeSendMessageCallback(message_guid, |
| SharingSendMessageResult::kInternalError, |
| /*response=*/nullptr); |
| return base::NullCallback(); |
| } |
| |
| content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE}) |
| ->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&SharingMessageSender::InvokeSendMessageCallback, |
| weak_ptr_factory_.GetWeakPtr(), message_guid, |
| SharingSendMessageResult::kAckTimeout, |
| /*response=*/nullptr), |
| response_timeout); |
| |
| message.set_sender_guid(local_device_info->guid()); |
| message.set_sender_device_name( |
| send_tab_to_self::GetSharingDeviceNames(local_device_info).full_name); |
| |
| TRACE_EVENT_NESTABLE_ASYNC_BEGIN0("sharing", "Sharing.DoSendMessage", |
| TRACE_ID_LOCAL(trace_id)); |
| delegate->DoSendMessageToDevice( |
| device, response_timeout, std::move(message), |
| base::BindOnce(&SharingMessageSender::OnMessageSent, |
| weak_ptr_factory_.GetWeakPtr(), message_guid)); |
| |
| return base::BindOnce(&SharingMessageSender::InvokeSendMessageCallback, |
| weak_ptr_factory_.GetWeakPtr(), message_guid, |
| SharingSendMessageResult::kCancelled, nullptr); |
| } |
| |
| void SharingMessageSender::OnMessageSent(const std::string& message_guid, |
| SharingSendMessageResult result, |
| std::optional<std::string> message_id, |
| SharingChannelType channel_type) { |
| auto metadata_iter = message_metadata_.find(message_guid); |
| DCHECK(metadata_iter != message_metadata_.end()); |
| TRACE_EVENT_NESTABLE_ASYNC_END1( |
| "sharing", "Sharing.DoSendMessage", |
| TRACE_ID_LOCAL(metadata_iter->second.trace_id), "result", |
| SharingSendMessageResultToString(result)); |
| metadata_iter->second.channel_type = channel_type; |
| if (result != SharingSendMessageResult::kSuccessful) { |
| InvokeSendMessageCallback(message_guid, result, |
| /*response=*/nullptr); |
| return; |
| } |
| |
| // Got a new message id, store it for later. |
| message_guids_.emplace(*message_id, message_guid); |
| |
| // Check if we got the ack while waiting for the FCM response. |
| auto cached_iter = cached_ack_response_messages_.find(*message_id); |
| if (cached_iter != cached_ack_response_messages_.end()) { |
| OnAckReceived(*message_id, std::move(cached_iter->second)); |
| cached_ack_response_messages_.erase(cached_iter); |
| } |
| } |
| |
| void SharingMessageSender::OnAckReceived( |
| const std::string& message_id, |
| std::unique_ptr<components_sharing_message::ResponseMessage> response) { |
| TRACE_EVENT0("sharing", "SharingMessageSender::OnAckReceived"); |
| auto guid_iter = message_guids_.find(message_id); |
| if (guid_iter == message_guids_.end()) { |
| // We don't have the guid yet, store the response until we receive it. |
| cached_ack_response_messages_.emplace(message_id, std::move(response)); |
| return; |
| } |
| |
| std::string message_guid = std::move(guid_iter->second); |
| message_guids_.erase(guid_iter); |
| |
| auto metadata_iter = message_metadata_.find(message_guid); |
| DCHECK(metadata_iter != message_metadata_.end()); |
| |
| InvokeSendMessageCallback(message_guid, SharingSendMessageResult::kSuccessful, |
| std::move(response)); |
| |
| message_metadata_.erase(metadata_iter); |
| } |
| |
| void SharingMessageSender::RegisterSendDelegate( |
| DelegateType type, |
| std::unique_ptr<SendMessageDelegate> delegate) { |
| auto result = send_delegates_.emplace(type, std::move(delegate)); |
| DCHECK(result.second) << "Delegate type already registered"; |
| } |
| |
| SharingFCMSender* SharingMessageSender::GetFCMSenderForTesting() const { |
| auto delegate_iter = send_delegates_.find(DelegateType::kFCM); |
| DCHECK(delegate_iter != send_delegates_.end()); |
| DCHECK(delegate_iter->second); |
| return static_cast<SharingFCMSender*>(delegate_iter->second.get()); |
| } |
| |
| void SharingMessageSender::InvokeSendMessageCallback( |
| const std::string& message_guid, |
| SharingSendMessageResult result, |
| std::unique_ptr<components_sharing_message::ResponseMessage> response) { |
| auto iter = message_metadata_.find(message_guid); |
| if (iter == message_metadata_.end() || !iter->second.callback) |
| return; |
| |
| SentMessageMetadata& metadata = iter->second; |
| |
| std::move(metadata.callback).Run(result, std::move(response)); |
| |
| LogSendSharingMessageResult(metadata.type, metadata.receiver_device_platform, |
| metadata.channel_type, |
| metadata.receiver_pulse_interval, result); |
| TRACE_EVENT_NESTABLE_ASYNC_END1("sharing", "SharingMessageSender.SendMessage", |
| TRACE_ID_LOCAL(metadata.trace_id), "result", |
| SharingSendMessageResultToString(result)); |
| } |
| |
| SharingMessageSender::SentMessageMetadata::SentMessageMetadata( |
| ResponseCallback callback, |
| base::TimeTicks timestamp, |
| components_sharing_message::MessageType type, |
| SharingDevicePlatform receiver_device_platform, |
| int trace_id, |
| SharingChannelType channel_type, |
| base::TimeDelta receiver_pulse_interval) |
| : callback(std::move(callback)), |
| timestamp(timestamp), |
| type(type), |
| receiver_device_platform(receiver_device_platform), |
| trace_id(trace_id), |
| channel_type(channel_type), |
| receiver_pulse_interval(receiver_pulse_interval) {} |
| |
| SharingMessageSender::SentMessageMetadata::SentMessageMetadata( |
| SentMessageMetadata&& other) = default; |
| |
| SharingMessageSender::SentMessageMetadata& |
| SharingMessageSender::SentMessageMetadata::operator=( |
| SentMessageMetadata&& other) = default; |
| |
| SharingMessageSender::SentMessageMetadata::~SentMessageMetadata() = default; |