| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "components/sync/driver/device_info_sync_service.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <utility> | |
| 11 | |
| 12 #include "base/memory/ptr_util.h" | |
| 13 #include "base/time/time.h" | |
| 14 #include "components/sync/api/sync_change.h" | |
| 15 #include "components/sync/base/time.h" | |
| 16 #include "components/sync/driver/device_info_util.h" | |
| 17 #include "components/sync/driver/local_device_info_provider.h" | |
| 18 #include "components/sync/protocol/sync.pb.h" | |
| 19 | |
| 20 namespace sync_driver { | |
| 21 | |
| 22 using base::Time; | |
| 23 using base::TimeDelta; | |
| 24 using syncer::ModelType; | |
| 25 using syncer::SyncChange; | |
| 26 using syncer::SyncChangeList; | |
| 27 using syncer::SyncChangeProcessor; | |
| 28 using syncer::SyncData; | |
| 29 using syncer::SyncDataList; | |
| 30 using syncer::SyncErrorFactory; | |
| 31 using syncer::SyncMergeResult; | |
| 32 | |
| 33 DeviceInfoSyncService::DeviceInfoSyncService( | |
| 34 LocalDeviceInfoProvider* local_device_info_provider) | |
| 35 : local_device_info_provider_(local_device_info_provider) { | |
| 36 DCHECK(local_device_info_provider); | |
| 37 } | |
| 38 | |
| 39 DeviceInfoSyncService::~DeviceInfoSyncService() {} | |
| 40 | |
| 41 SyncMergeResult DeviceInfoSyncService::MergeDataAndStartSyncing( | |
| 42 ModelType type, | |
| 43 const SyncDataList& initial_sync_data, | |
| 44 std::unique_ptr<SyncChangeProcessor> sync_processor, | |
| 45 std::unique_ptr<SyncErrorFactory> error_handler) { | |
| 46 DCHECK(sync_processor.get()); | |
| 47 DCHECK(error_handler.get()); | |
| 48 DCHECK_EQ(type, syncer::DEVICE_INFO); | |
| 49 | |
| 50 DCHECK(!IsSyncing()); | |
| 51 | |
| 52 sync_processor_ = std::move(sync_processor); | |
| 53 error_handler_ = std::move(error_handler); | |
| 54 | |
| 55 // Initialization should be completed before this type is enabled | |
| 56 // and local device info must be available. | |
| 57 const DeviceInfo* local_device_info = | |
| 58 local_device_info_provider_->GetLocalDeviceInfo(); | |
| 59 DCHECK(local_device_info != NULL); | |
| 60 | |
| 61 // Indicates whether a local device has been added or updated. | |
| 62 // |change_type| defaults to ADD and might be changed to | |
| 63 // UPDATE to INVALID down below if the initial data contains | |
| 64 // data matching the local device ID. | |
| 65 SyncChange::SyncChangeType change_type = SyncChange::ACTION_ADD; | |
| 66 TimeDelta pulse_delay; | |
| 67 size_t num_items_new = 0; | |
| 68 size_t num_items_updated = 0; | |
| 69 | |
| 70 // Iterate over all initial sync data and copy it to the cache. | |
| 71 for (SyncDataList::const_iterator iter = initial_sync_data.begin(); | |
| 72 iter != initial_sync_data.end(); ++iter) { | |
| 73 DCHECK_EQ(syncer::DEVICE_INFO, iter->GetDataType()); | |
| 74 | |
| 75 const std::string& id = iter->GetSpecifics().device_info().cache_guid(); | |
| 76 | |
| 77 if (id == local_device_info->guid()) { | |
| 78 // |initial_sync_data| contains data matching the local device. | |
| 79 std::unique_ptr<DeviceInfo> synced_local_device_info = | |
| 80 base::WrapUnique(CreateDeviceInfo(*iter)); | |
| 81 | |
| 82 pulse_delay = DeviceInfoUtil::CalculatePulseDelay( | |
| 83 GetLastUpdateTime(*iter), Time::Now()); | |
| 84 // Store the synced device info for the local device only if | |
| 85 // it is the same as the local info. Otherwise store the local | |
| 86 // device info and issue a change further below after finishing | |
| 87 // processing the |initial_sync_data|. | |
| 88 if (synced_local_device_info->Equals(*local_device_info) && | |
| 89 !pulse_delay.is_zero()) { | |
| 90 change_type = SyncChange::ACTION_INVALID; | |
| 91 } else { | |
| 92 num_items_updated++; | |
| 93 change_type = SyncChange::ACTION_UPDATE; | |
| 94 continue; | |
| 95 } | |
| 96 } else { | |
| 97 // A new device that doesn't match the local device. | |
| 98 num_items_new++; | |
| 99 } | |
| 100 | |
| 101 StoreSyncData(id, *iter); | |
| 102 } | |
| 103 | |
| 104 syncer::SyncMergeResult result(type); | |
| 105 | |
| 106 // If the SyncData for the local device is new or different then send it | |
| 107 // immediately, otherwise wait until the pulse interval has elapsed from the | |
| 108 // previous update. Regardless of the branch here we setup a timer loop with | |
| 109 // SendLocalData such that we continue pulsing every interval. | |
| 110 if (change_type == SyncChange::ACTION_INVALID) { | |
| 111 pulse_timer_.Start( | |
| 112 FROM_HERE, pulse_delay, | |
| 113 base::Bind(&DeviceInfoSyncService::SendLocalData, | |
| 114 base::Unretained(this), SyncChange::ACTION_UPDATE)); | |
| 115 } else { | |
| 116 SendLocalData(change_type); | |
| 117 } | |
| 118 | |
| 119 result.set_num_items_before_association(1); | |
| 120 result.set_num_items_after_association(all_data_.size()); | |
| 121 result.set_num_items_added(num_items_new); | |
| 122 result.set_num_items_modified(num_items_updated); | |
| 123 result.set_num_items_deleted(0); | |
| 124 | |
| 125 NotifyObservers(); | |
| 126 | |
| 127 return result; | |
| 128 } | |
| 129 | |
| 130 bool DeviceInfoSyncService::IsSyncing() const { | |
| 131 return !all_data_.empty(); | |
| 132 } | |
| 133 | |
| 134 void DeviceInfoSyncService::StopSyncing(syncer::ModelType type) { | |
| 135 bool was_syncing = IsSyncing(); | |
| 136 | |
| 137 pulse_timer_.Stop(); | |
| 138 all_data_.clear(); | |
| 139 sync_processor_.reset(); | |
| 140 error_handler_.reset(); | |
| 141 | |
| 142 if (was_syncing) { | |
| 143 NotifyObservers(); | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 SyncDataList DeviceInfoSyncService::GetAllSyncData( | |
| 148 syncer::ModelType type) const { | |
| 149 SyncDataList list; | |
| 150 | |
| 151 for (SyncDataMap::const_iterator iter = all_data_.begin(); | |
| 152 iter != all_data_.end(); ++iter) { | |
| 153 list.push_back(iter->second); | |
| 154 } | |
| 155 | |
| 156 return list; | |
| 157 } | |
| 158 | |
| 159 syncer::SyncError DeviceInfoSyncService::ProcessSyncChanges( | |
| 160 const tracked_objects::Location& from_here, | |
| 161 const SyncChangeList& change_list) { | |
| 162 syncer::SyncError error; | |
| 163 | |
| 164 DCHECK(local_device_info_provider_->GetLocalDeviceInfo()); | |
| 165 const std::string& local_device_id = | |
| 166 local_device_info_provider_->GetLocalDeviceInfo()->guid(); | |
| 167 | |
| 168 bool has_changes = false; | |
| 169 | |
| 170 // Iterate over all chanages and merge entries. | |
| 171 for (SyncChangeList::const_iterator iter = change_list.begin(); | |
| 172 iter != change_list.end(); ++iter) { | |
| 173 const SyncData& sync_data = iter->sync_data(); | |
| 174 DCHECK_EQ(syncer::DEVICE_INFO, sync_data.GetDataType()); | |
| 175 | |
| 176 const std::string& client_id = | |
| 177 sync_data.GetSpecifics().device_info().cache_guid(); | |
| 178 // Ignore device info matching the local device. | |
| 179 if (local_device_id == client_id) { | |
| 180 DVLOG(1) << "Ignoring sync changes for the local DEVICE_INFO"; | |
| 181 continue; | |
| 182 } | |
| 183 | |
| 184 if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) { | |
| 185 has_changes = true; | |
| 186 DeleteSyncData(client_id); | |
| 187 } else if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE || | |
| 188 iter->change_type() == syncer::SyncChange::ACTION_ADD) { | |
| 189 has_changes = true; | |
| 190 StoreSyncData(client_id, sync_data); | |
| 191 } else { | |
| 192 error.Reset(FROM_HERE, "Invalid action received.", syncer::DEVICE_INFO); | |
| 193 } | |
| 194 } | |
| 195 | |
| 196 if (has_changes) { | |
| 197 NotifyObservers(); | |
| 198 } | |
| 199 | |
| 200 return error; | |
| 201 } | |
| 202 | |
| 203 std::unique_ptr<DeviceInfo> DeviceInfoSyncService::GetDeviceInfo( | |
| 204 const std::string& client_id) const { | |
| 205 SyncDataMap::const_iterator iter = all_data_.find(client_id); | |
| 206 if (iter == all_data_.end()) { | |
| 207 return std::unique_ptr<DeviceInfo>(); | |
| 208 } | |
| 209 | |
| 210 return base::WrapUnique(CreateDeviceInfo(iter->second)); | |
| 211 } | |
| 212 | |
| 213 ScopedVector<DeviceInfo> DeviceInfoSyncService::GetAllDeviceInfo() const { | |
| 214 ScopedVector<DeviceInfo> list; | |
| 215 | |
| 216 for (SyncDataMap::const_iterator iter = all_data_.begin(); | |
| 217 iter != all_data_.end(); ++iter) { | |
| 218 list.push_back(CreateDeviceInfo(iter->second)); | |
| 219 } | |
| 220 | |
| 221 return list; | |
| 222 } | |
| 223 | |
| 224 void DeviceInfoSyncService::AddObserver(Observer* observer) { | |
| 225 observers_.AddObserver(observer); | |
| 226 } | |
| 227 | |
| 228 void DeviceInfoSyncService::RemoveObserver(Observer* observer) { | |
| 229 observers_.RemoveObserver(observer); | |
| 230 } | |
| 231 | |
| 232 int DeviceInfoSyncService::CountActiveDevices() const { | |
| 233 return CountActiveDevices(Time::Now()); | |
| 234 } | |
| 235 | |
| 236 void DeviceInfoSyncService::NotifyObservers() { | |
| 237 FOR_EACH_OBSERVER(Observer, observers_, OnDeviceInfoChange()); | |
| 238 } | |
| 239 | |
| 240 SyncData DeviceInfoSyncService::CreateLocalData(const DeviceInfo* info) { | |
| 241 sync_pb::EntitySpecifics entity; | |
| 242 sync_pb::DeviceInfoSpecifics& specifics = *entity.mutable_device_info(); | |
| 243 | |
| 244 specifics.set_cache_guid(info->guid()); | |
| 245 specifics.set_client_name(info->client_name()); | |
| 246 specifics.set_chrome_version(info->chrome_version()); | |
| 247 specifics.set_sync_user_agent(info->sync_user_agent()); | |
| 248 specifics.set_device_type(info->device_type()); | |
| 249 specifics.set_signin_scoped_device_id(info->signin_scoped_device_id()); | |
| 250 specifics.set_last_updated_timestamp(syncer::TimeToProtoTime(Time::Now())); | |
| 251 | |
| 252 return CreateLocalData(entity); | |
| 253 } | |
| 254 | |
| 255 SyncData DeviceInfoSyncService::CreateLocalData( | |
| 256 const sync_pb::EntitySpecifics& entity) { | |
| 257 const sync_pb::DeviceInfoSpecifics& specifics = entity.device_info(); | |
| 258 return SyncData::CreateLocalData(DeviceInfoUtil::SpecificsToTag(specifics), | |
| 259 specifics.client_name(), entity); | |
| 260 } | |
| 261 | |
| 262 DeviceInfo* DeviceInfoSyncService::CreateDeviceInfo( | |
| 263 const syncer::SyncData& sync_data) { | |
| 264 const sync_pb::DeviceInfoSpecifics& specifics = | |
| 265 sync_data.GetSpecifics().device_info(); | |
| 266 | |
| 267 return new DeviceInfo(specifics.cache_guid(), specifics.client_name(), | |
| 268 specifics.chrome_version(), specifics.sync_user_agent(), | |
| 269 specifics.device_type(), | |
| 270 specifics.signin_scoped_device_id()); | |
| 271 } | |
| 272 | |
| 273 void DeviceInfoSyncService::StoreSyncData(const std::string& client_id, | |
| 274 const SyncData& sync_data) { | |
| 275 DVLOG(1) << "Storing DEVICE_INFO for " | |
| 276 << sync_data.GetSpecifics().device_info().client_name() | |
| 277 << " with ID " << client_id; | |
| 278 all_data_[client_id] = sync_data; | |
| 279 } | |
| 280 | |
| 281 void DeviceInfoSyncService::DeleteSyncData(const std::string& client_id) { | |
| 282 SyncDataMap::iterator iter = all_data_.find(client_id); | |
| 283 if (iter != all_data_.end()) { | |
| 284 DVLOG(1) << "Deleting DEVICE_INFO for " | |
| 285 << iter->second.GetSpecifics().device_info().client_name() | |
| 286 << " with ID " << client_id; | |
| 287 all_data_.erase(iter); | |
| 288 } | |
| 289 } | |
| 290 | |
| 291 void DeviceInfoSyncService::SendLocalData( | |
| 292 const SyncChange::SyncChangeType change_type) { | |
| 293 DCHECK_NE(change_type, SyncChange::ACTION_INVALID); | |
| 294 DCHECK(sync_processor_); | |
| 295 | |
| 296 const DeviceInfo* device_info = | |
| 297 local_device_info_provider_->GetLocalDeviceInfo(); | |
| 298 const SyncData& data = CreateLocalData(device_info); | |
| 299 StoreSyncData(device_info->guid(), data); | |
| 300 | |
| 301 SyncChangeList change_list; | |
| 302 change_list.push_back(SyncChange(FROM_HERE, change_type, data)); | |
| 303 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list); | |
| 304 | |
| 305 pulse_timer_.Start( | |
| 306 FROM_HERE, DeviceInfoUtil::kPulseInterval, | |
| 307 base::Bind(&DeviceInfoSyncService::SendLocalData, base::Unretained(this), | |
| 308 SyncChange::ACTION_UPDATE)); | |
| 309 } | |
| 310 | |
| 311 int DeviceInfoSyncService::CountActiveDevices(const Time now) const { | |
| 312 return std::count_if(all_data_.begin(), all_data_.end(), | |
| 313 [now](SyncDataMap::const_reference pair) { | |
| 314 return DeviceInfoUtil::IsActive( | |
| 315 GetLastUpdateTime(pair.second), now); | |
| 316 }); | |
| 317 } | |
| 318 | |
| 319 // static | |
| 320 Time DeviceInfoSyncService::GetLastUpdateTime(const SyncData& device_info) { | |
| 321 if (device_info.GetSpecifics().device_info().has_last_updated_timestamp()) { | |
| 322 return syncer::ProtoTimeToTime( | |
| 323 device_info.GetSpecifics().device_info().last_updated_timestamp()); | |
| 324 } else if (!device_info.IsLocal()) { | |
| 325 // If there is no |last_updated_timestamp| present, fallback to mod time. | |
| 326 return syncer::SyncDataRemote(device_info).GetModifiedTime(); | |
| 327 } else { | |
| 328 // We shouldn't reach this point for remote data, so this means we're likely | |
| 329 // looking at the local device info. Using a long ago time is perfect, since | |
| 330 // the desired behavior is to update/pulse our data as soon as possible. | |
| 331 return Time(); | |
| 332 } | |
| 333 } | |
| 334 | |
| 335 } // namespace sync_driver | |
| OLD | NEW |