| // Copyright 2024 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/sync/service/local_data_migration_item_queue.h" | |
| #include "base/time/time.h" | |
| #include "components/sync/base/data_type.h" | |
| #include "components/sync/service/data_type_manager.h" | |
| #include "components/sync/service/sync_service.h" | |
| namespace syncer { | |
| namespace { | |
| // The time limit for activating the data type is set to 50 minutes, as this is | |
| // in line with the `SigninPromoTabHelper` waiting for a sign in event. | |
| constexpr base::TimeDelta kTimeLimitForActivatingDataType = base::Minutes(50); | |
| // Removes items which had been added to the queue before the time limit | |
| // has been exceeded, or have a data type that is non-preferred. | |
| void RemoveExpiredItemsAndItemsOfNonPreferredDataTypes( | |
| std::map<LocalDataItemModel::DataId, std::tuple<DataType, base::Time>>& | |
| items, | |
| const DataTypeSet& preferred_data_types, | |
| const base::Time& current_time_stamp) { | |
| // Use this to keep the items for which the data type is not active yet. | |
| std::map<LocalDataItemModel::DataId, std::tuple<DataType, base::Time>> | |
| items_to_keep; | |
| for (auto& item : items) { | |
| DataType data_type = std::get<0>(item.second); | |
| base::Time initialized_time_stamp = std::get<1>(item.second); | |
| LocalDataItemModel::DataId data_id = item.first; | |
| // Do not include the item if the time limit since the initialization | |
| // of its move has been exceeded. This can happen for example if a user | |
| // with custom passphrase enabled signs in after a sign in promo which | |
| // initializes this queue, but the user still has to enter their | |
| // passphrase. As they may forget that entering their passphrase would | |
| // move the data, we do nothing instead. Also, remove items which have a | |
| // data type which is non-preferred. | |
| if (current_time_stamp - initialized_time_stamp < | |
| kTimeLimitForActivatingDataType && | |
| preferred_data_types.Has(data_type)) { | |
| items_to_keep[data_id] = std::move(item.second); | |
| } | |
| } | |
| items = std::move(items_to_keep); | |
| } | |
| // Returns those items which have a data type that is active, so that their | |
| // migration to account storage can be triggered. The items for which the data | |
| // type is not active are kept in `items` in case it will be activated later, | |
| // e.g. if the user enters their custom passphrase. | |
| std::map<DataType, std::vector<LocalDataItemModel::DataId>> | |
| MoveItemsOfActiveDataTypesToVector( | |
| std::map<LocalDataItemModel::DataId, std::tuple<DataType, base::Time>>& | |
| items, | |
| const DataTypeSet& active_data_types) { | |
| // Use this to keep the items for which the data type is not active yet. | |
| std::map<LocalDataItemModel::DataId, std::tuple<DataType, base::Time>> | |
| items_to_keep; | |
| // Items that are added here will be migrated to account storage. | |
| std::map<DataType, std::vector<LocalDataItemModel::DataId>> result; | |
| for (auto& item : items) { | |
| DataType data_type = std::get<0>(item.second); | |
| LocalDataItemModel::DataId data_id = item.first; | |
| // Only include those items in the vector for which the data type is already | |
| // active. | |
| if (active_data_types.Has(data_type)) { | |
| result[data_type].push_back(data_id); | |
| } else { | |
| items_to_keep[data_id] = std::move(item.second); | |
| } | |
| } | |
| items = std::move(items_to_keep); | |
| return result; | |
| } | |
| } // namespace | |
| LocalDataMigrationItemQueue::LocalDataMigrationItemQueue( | |
| SyncService* sync_service, | |
| DataTypeManager* data_type_manager) | |
| : sync_service_(sync_service), | |
| data_type_manager_(data_type_manager), | |
| clock_(base::DefaultClock::GetInstance()) { | |
| CHECK(sync_service); | |
| CHECK(data_type_manager); | |
| sync_service_observer_.Observe(sync_service_); | |
| } | |
| LocalDataMigrationItemQueue::~LocalDataMigrationItemQueue() = default; | |
| void LocalDataMigrationItemQueue:: | |
| TriggerLocalDataMigrationForItemsWhenTypeBecomesActive( | |
| DataType data_type, | |
| std::vector<LocalDataItemModel::DataId> items) { | |
| base::Time current_timestamp = clock_->Now(); | |
| for (const auto& item : items) { | |
| items_[item] = std::tuple(data_type, current_timestamp); | |
| } | |
| // Fire once in case the sync service is already active. | |
| OnStateChanged(sync_service_); | |
| } | |
| void LocalDataMigrationItemQueue::OnStateChanged(SyncService* sync_service) { | |
| CHECK_EQ(sync_service, sync_service_); | |
| // Do not move the data if the user has consented to sync in the meantime. In | |
| // this case, simply leave the data in local storage. | |
| if (sync_service->HasSyncConsent()) { | |
| items_.clear(); | |
| return; | |
| } | |
| switch (sync_service->GetTransportState()) { | |
| case SyncService::TransportState::DISABLED: | |
| case SyncService::TransportState::PAUSED: { | |
| items_.clear(); | |
| return; | |
| } | |
| case SyncService::TransportState::START_DEFERRED: | |
| case SyncService::TransportState::INITIALIZING: | |
| case SyncService::TransportState::PENDING_DESIRED_CONFIGURATION: | |
| case SyncService::TransportState::CONFIGURING: | |
| // Keep waiting if the sync service is not active yet. | |
| return; | |
| case SyncService::TransportState::ACTIVE: | |
| break; | |
| } | |
| CHECK_EQ(DataTypeManager::CONFIGURED, data_type_manager_->state()); | |
| RemoveExpiredItemsAndItemsOfNonPreferredDataTypes( | |
| items_, sync_service->GetPreferredDataTypes(), clock_->Now()); | |
| std::map<DataType, std::vector<LocalDataItemModel::DataId>> items_to_migrate = | |
| MoveItemsOfActiveDataTypesToVector(items_, | |
| sync_service->GetActiveDataTypes()); | |
| if (items_to_migrate.empty()) { | |
| return; | |
| } | |
| // Use the data type manager directly rather than | |
| // `SyncService::TriggerLocalDataMigration()`, since this object is used for a | |
| // single data item move operation from the sign in promo. This is different | |
| // from batch upload, so we shouldn't record the batch upload histogram in | |
| // that case. | |
| data_type_manager_->TriggerLocalDataMigrationForItems(items_to_migrate); | |
| } | |
| void LocalDataMigrationItemQueue::OnSyncShutdown(SyncService* sync_service) { | |
| items_.clear(); | |
| } | |
| void LocalDataMigrationItemQueue::SetClockForTesting(base::Clock* clock) { | |
| clock_ = clock; | |
| } | |
| size_t LocalDataMigrationItemQueue::GetItemsCountForTesting() const { | |
| return items_.size(); | |
| } | |
| } // namespace syncer |