blob: a7a33377a87be6e290080d64eded75330792c5fc [file]
// 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/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;
}
return SharingTargetDeviceInfo(
device_info->guid(), syncer::GetDeviceDisplayNames(device_info).full_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_);
std::erase_if(devices, [accepted_features, can_send_via_sender_id](
const syncer::DeviceInfo* device) {
// 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();
});
const syncer::DeviceInfo* local_device =
local_device_info_provider_->GetLocalDeviceInfo();
std::optional<std::string> local_full_name;
if (local_device) {
local_full_name = syncer::GetDeviceDisplayNames(local_device).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());
});
}