| // 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 "components/sharing_message/sharing_device_source_sync.h" | |
| #include <vector> | |
| #include "base/containers/to_vector.h" | |
| #include "base/functional/callback.h" | |
| #include "base/stl_util.h" | |
| #include "base/task/thread_pool.h" | |
| #include "base/trace_event/trace_event.h" | |
| #include "components/sharing_message/features.h" | |
| #include "components/sharing_message/proto/sharing_message.pb.h" | |
| #include "components/sharing_message/sharing_constants.h" | |
| #include "components/sharing_message/sharing_target_device_info.h" | |
| #include "components/sharing_message/sharing_utils.h" | |
| #include "components/sync/base/features.h" | |
| #include "components/sync/service/sync_service.h" | |
| #include "components/sync_device_info/device_info.h" | |
| #include "components/sync_device_info/device_name_util.h" | |
| #include "components/sync_device_info/local_device_info_provider.h" | |
| #include "components/sync_device_info/local_device_info_util.h" | |
| namespace { | |
| bool IsStale(const syncer::DeviceInfo& device) { | |
| const base::Time min_updated_time = | |
| base::Time::Now() - kSharingDeviceExpiration; | |
| return device.last_updated_timestamp() < min_updated_time; | |
| } | |
| } // namespace | |
| SharingDeviceSourceSync::SharingDeviceSourceSync( | |
| syncer::SyncService* sync_service, | |
| syncer::LocalDeviceInfoProvider* local_device_info_provider, | |
| syncer::DeviceInfoTracker* device_info_tracker) | |
| : sync_service_(sync_service), | |
| local_device_info_provider_(local_device_info_provider), | |
| device_info_tracker_(device_info_tracker) { | |
| if (!device_info_tracker_->IsSyncing()) { | |
| device_info_tracker_->AddObserver(this); | |
| } | |
| if (!local_device_info_provider_->GetLocalDeviceInfo()) { | |
| local_device_info_ready_subscription_ = | |
| local_device_info_provider_->RegisterOnInitializedCallback( | |
| base::BindRepeating( | |
| &SharingDeviceSourceSync::OnLocalDeviceInfoProviderReady, | |
| weak_ptr_factory_.GetWeakPtr())); | |
| } | |
| } | |
| SharingDeviceSourceSync::~SharingDeviceSourceSync() { | |
| device_info_tracker_->RemoveObserver(this); | |
| } | |
| std::optional<SharingTargetDeviceInfo> SharingDeviceSourceSync::GetDeviceByGuid( | |
| const std::string& guid) { | |
| if (!IsSyncEnabledForSharing(sync_service_)) { | |
| return std::nullopt; | |
| } | |
| const syncer::DeviceInfo* device_info = | |
| device_info_tracker_->GetDeviceInfo(guid); | |
| if (!device_info) { | |
| return std::nullopt; | |
| } | |
| std::string display_name = | |
| base::FeatureList::IsEnabled(syncer::kSyncSimplifyDeviceNaming) | |
| ? syncer::GetDeviceDisplayName(device_info) | |
| : syncer::GetDisplayNameCandidates(device_info).fallback_full_name; | |
| return SharingTargetDeviceInfo( | |
| device_info->guid(), std::move(display_name), | |
| GetDevicePlatform(*device_info), device_info->pulse_interval(), | |
| device_info->form_factor(), device_info->last_updated_timestamp()); | |
| } | |
| std::vector<SharingTargetDeviceInfo> | |
| SharingDeviceSourceSync::GetDeviceCandidates( | |
| syncer::DeviceInfo::SharingFeature required_feature) { | |
| if (!IsSyncEnabledForSharing(sync_service_) || !IsReady()) { | |
| return {}; | |
| } | |
| return ConvertAndDeduplicateDevices(FilterDeviceCandidates( | |
| device_info_tracker_->GetAllDeviceInfo(), required_feature)); | |
| } | |
| bool SharingDeviceSourceSync::IsReady() { | |
| return IsSyncDisabledForSharing(sync_service_) || | |
| (device_info_tracker_->IsSyncing() && | |
| local_device_info_provider_->GetLocalDeviceInfo()); | |
| } | |
| void SharingDeviceSourceSync::OnDeviceInfoChange() { | |
| TRACE_EVENT0("sharing", "SharingDeviceSourceSync::OnDeviceInfoChange"); | |
| if (device_info_tracker_->IsSyncing()) { | |
| device_info_tracker_->RemoveObserver(this); | |
| } | |
| MaybeRunReadyCallbacks(); | |
| } | |
| void SharingDeviceSourceSync::SetDeviceInfoTrackerForTesting( | |
| syncer::DeviceInfoTracker* tracker) { | |
| device_info_tracker_->RemoveObserver(this); | |
| device_info_tracker_ = tracker; | |
| if (!device_info_tracker_->IsSyncing()) { | |
| device_info_tracker_->AddObserver(this); | |
| } | |
| MaybeRunReadyCallbacks(); | |
| } | |
| void SharingDeviceSourceSync::OnLocalDeviceInfoProviderReady() { | |
| DCHECK(local_device_info_provider_->GetLocalDeviceInfo()); | |
| local_device_info_ready_subscription_ = {}; | |
| MaybeRunReadyCallbacks(); | |
| } | |
| std::vector<const syncer::DeviceInfo*> | |
| SharingDeviceSourceSync::FilterDeviceCandidates( | |
| std::vector<const syncer::DeviceInfo*> devices, | |
| syncer::DeviceInfo::SharingFeature required_feature) const { | |
| std::set<syncer::DeviceInfo::SharingFeature> accepted_features{ | |
| required_feature}; | |
| bool can_send_via_sender_id = CanSendViaSenderID(sync_service_); | |
| const syncer::DeviceInfo* local_device = | |
| local_device_info_provider_->GetLocalDeviceInfo(); | |
| std::erase_if(devices, [accepted_features, can_send_via_sender_id, | |
| local_device](const syncer::DeviceInfo* device) { | |
| // Filter out local device. | |
| if (local_device && device->guid() == local_device->guid()) { | |
| return true; | |
| } | |
| // Checks if |last_updated_timestamp| is not too old. | |
| if (IsStale(*device)) { | |
| return true; | |
| } | |
| // Checks if device has SharingInfo. | |
| if (!device->sharing_info()) { | |
| return true; | |
| } | |
| // Checks if message can be sent via either VAPID or sender ID. | |
| auto& sender_id_target_info = device->sharing_info()->sender_id_target_info; | |
| bool sender_id_channel_valid = | |
| (can_send_via_sender_id && !sender_id_target_info.fcm_token.empty() && | |
| !sender_id_target_info.p256dh.empty() && | |
| !sender_id_target_info.auth_secret.empty()); | |
| if (!sender_id_channel_valid) { | |
| return true; | |
| } | |
| // Checks whether `device` supports any of `accepted_features`. | |
| return base::STLSetIntersection< | |
| std::vector<syncer::DeviceInfo::SharingFeature>>( | |
| device->sharing_info()->enabled_features, accepted_features) | |
| .empty(); | |
| }); | |
| return devices; | |
| } | |
| std::vector<SharingTargetDeviceInfo> | |
| SharingDeviceSourceSync::ConvertAndDeduplicateDevices( | |
| std::vector<const syncer::DeviceInfo*> devices) const { | |
| // Sort the devices so the most recently modified devices are first. | |
| std::sort(devices.begin(), devices.end(), | |
| [](const auto& device1, const auto& device2) { | |
| return device1->last_updated_timestamp() > | |
| device2->last_updated_timestamp(); | |
| }); | |
| if (base::FeatureList::IsEnabled(syncer::kSyncSimplifyDeviceNaming)) { | |
| // Resolve display names for the filtered list by using the preferred name. | |
| return base::ToVector(devices, [](const auto& device) { | |
| return SharingTargetDeviceInfo( | |
| device->guid(), syncer::GetDeviceDisplayName(device), | |
| GetDevicePlatform(*device), device->pulse_interval(), | |
| device->form_factor(), device->last_updated_timestamp()); | |
| }); | |
| } | |
| const syncer::DeviceInfo* local_device = | |
| local_device_info_provider_->GetLocalDeviceInfo(); | |
| std::optional<std::string> local_full_name; | |
| if (local_device) { | |
| local_full_name = | |
| syncer::GetDisplayNameCandidates(local_device).fallback_full_name; | |
| } | |
| // Resolve display names for the filtered list. This handles de-duplication | |
| // by name and chooses between short/full names based on collisions. | |
| std::vector<syncer::DeviceInfoWithName> device_names = | |
| syncer::DetermineDisplayNamesAndDeduplicate(devices, local_full_name); | |
| // Convert to SharingTargetDeviceInfo, filtering out any devices that were | |
| // de-duplicated by the naming utility. | |
| return base::ToVector(device_names, [](const auto& info) { | |
| return SharingTargetDeviceInfo( | |
| info.device->guid(), info.display_name, GetDevicePlatform(*info.device), | |
| info.device->pulse_interval(), info.device->form_factor(), | |
| info.device->last_updated_timestamp()); | |
| }); | |
| } |