// 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 #include #include #include #include "base/containers/cxx20_erase.h" #include "base/feature_list.h" #include "base/metrics/field_trial_params.h" #include "base/ranges/algorithm.h" #include "components/browser_sync/active_devices_provider_impl.h" #include "components/browser_sync/browser_sync_switches.h" #include "components/sync/base/model_type.h" namespace browser_sync { ActiveDevicesProviderImpl::ActiveDevicesProviderImpl( syncer::DeviceInfoTracker* device_info_tracker, base::Clock* clock) : device_info_tracker_(device_info_tracker), clock_(clock) { DCHECK(device_info_tracker_); device_info_tracker_->AddObserver(this); } ActiveDevicesProviderImpl::~ActiveDevicesProviderImpl() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK(callback_.is_null()); device_info_tracker_->RemoveObserver(this); } syncer::ActiveDevicesInvalidationInfo ActiveDevicesProviderImpl::CalculateInvalidationInfo( const std::string& local_cache_guid) const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); const std::vector> active_devices = GetActiveDevicesSortedByUpdateTime(); if (active_devices.empty()) { // This may happen if the engine is not initialized yet. In other cases, // |active_devices| must contain at least the local device. return syncer::ActiveDevicesInvalidationInfo::CreateUninitialized(); } std::vector all_fcm_registration_tokens; // List of interested data types for all other clients. syncer::ModelTypeSet all_interested_data_types; // FCM registration tokens with corresponding interested data types for all // the clients with enabled sync standalone invalidations. std::map fcm_token_and_interested_data_types; for (const std::unique_ptr& device : active_devices) { if (!local_cache_guid.empty() && device->guid() == local_cache_guid) { continue; } all_interested_data_types.PutAll(device->interested_data_types()); if (!device->fcm_registration_token().empty()) { // If there is a duplicate FCM registration token, use the latest one. To // achieve this, rely on sorted |active_devices| by update time. Two // DeviceInfo entities can have the same FCM registration token if the // sync engine was reset without signout. fcm_token_and_interested_data_types[device->fcm_registration_token()] = device->interested_data_types(); if (base::FeatureList::IsEnabled( switches::kSyncUseFCMRegistrationTokensList)) { all_fcm_registration_tokens.push_back(device->fcm_registration_token()); } } } // Do not send tokens if the list of active devices is huge. This is similar // to the case when the client doesn't know about other devices, so return an // empty list. Otherwise the client would return only a part of all active // clients and other clients might miss an invalidation. if (all_fcm_registration_tokens.size() > static_cast( switches::kSyncFCMRegistrationTokensListMaxSize.Get())) { all_fcm_registration_tokens.clear(); } return syncer::ActiveDevicesInvalidationInfo::Create( std::move(all_fcm_registration_tokens), all_interested_data_types, std::move(fcm_token_and_interested_data_types)); } void ActiveDevicesProviderImpl::SetActiveDevicesChangedCallback( ActiveDevicesChangedCallback callback) { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); // The |callback_| must not be replaced with another non-null |callback|. DCHECK(callback_.is_null() || callback.is_null()); callback_ = std::move(callback); } void ActiveDevicesProviderImpl::OnDeviceInfoChange() { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); if (callback_) { callback_.Run(); } } std::vector> ActiveDevicesProviderImpl::GetActiveDevicesSortedByUpdateTime() const { std::vector> all_devices = device_info_tracker_->GetAllDeviceInfo(); base::ranges::sort( all_devices, [](const std::unique_ptr& left_device, const std::unique_ptr& right_device) { return left_device->last_updated_timestamp() < right_device->last_updated_timestamp(); }); if (!base::FeatureList::IsEnabled( switches::kSyncFilterOutInactiveDevicesForSingleClient)) { return all_devices; } base::EraseIf( all_devices, [this](const std::unique_ptr& device) { const base::Time expected_expiration_time = device->last_updated_timestamp() + device->pulse_interval() + switches::kSyncActiveDeviceMargin.Get(); // If the device's expiration time hasn't been reached, then // it is considered active device. return expected_expiration_time <= clock_->Now(); }); return all_devices; } } // namespace browser_sync