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 "base/strings/stringprintf.h" 8#include "components/sync_driver/local_device_info_provider.h" 9#include "sync/api/sync_change.h" 10#include "sync/protocol/sync.pb.h" 11#include "sync/util/time.h" 12 13namespace sync_driver { 14 15using syncer::ModelType; 16using syncer::SyncChange; 17using syncer::SyncChangeList; 18using syncer::SyncChangeProcessor; 19using syncer::SyncData; 20using syncer::SyncDataList; 21using syncer::SyncErrorFactory; 22using syncer::SyncMergeResult; 23 24DeviceInfoSyncService::DeviceInfoSyncService( 25 LocalDeviceInfoProvider* local_device_info_provider) 26 : local_device_backup_time_(-1), 27 local_device_info_provider_(local_device_info_provider) { 28 DCHECK(local_device_info_provider); 29} 30 31DeviceInfoSyncService::~DeviceInfoSyncService() { 32} 33 34SyncMergeResult DeviceInfoSyncService::MergeDataAndStartSyncing( 35 ModelType type, 36 const SyncDataList& initial_sync_data, 37 scoped_ptr<SyncChangeProcessor> sync_processor, 38 scoped_ptr<SyncErrorFactory> error_handler) { 39 DCHECK(sync_processor.get()); 40 DCHECK(error_handler.get()); 41 DCHECK_EQ(type, syncer::DEVICE_INFO); 42 43 DCHECK(all_data_.empty()); 44 45 sync_processor_ = sync_processor.Pass(); 46 error_handler_ = error_handler.Pass(); 47 48 // Initialization should be completed before this type is enabled 49 // and local device info must be available. 50 const DeviceInfo* local_device_info = 51 local_device_info_provider_->GetLocalDeviceInfo(); 52 DCHECK(local_device_info != NULL); 53 54 // Indicates whether a local device has been added or updated. 55 // |change_type| defaults to ADD and might be changed to 56 // UPDATE to INVALID down below if the initial data contains 57 // data matching the local device ID. 58 SyncChange::SyncChangeType change_type = SyncChange::ACTION_ADD; 59 size_t num_items_new = 0; 60 size_t num_items_updated = 0; 61 62 // Iterate over all initial sync data and copy it to the cache. 63 for (SyncDataList::const_iterator iter = initial_sync_data.begin(); 64 iter != initial_sync_data.end(); 65 ++iter) { 66 DCHECK_EQ(syncer::DEVICE_INFO, iter->GetDataType()); 67 68 const std::string& id = iter->GetSpecifics().device_info().cache_guid(); 69 70 if (id == local_device_info->guid()) { 71 // |initial_sync_data| contains data matching the local device. 72 scoped_ptr<DeviceInfo> synced_local_device_info = 73 make_scoped_ptr(CreateDeviceInfo(*iter)); 74 75 // Retrieve local device backup timestamp value from the sync data. 76 bool has_synced_backup_time = 77 iter->GetSpecifics().device_info().has_backup_timestamp(); 78 int64 synced_backup_time = 79 has_synced_backup_time 80 ? iter->GetSpecifics().device_info().backup_timestamp() 81 : -1; 82 83 // Overwrite |local_device_backup_time_| with this value if it 84 // hasn't been set yet. 85 if (!has_local_device_backup_time() && has_synced_backup_time) { 86 set_local_device_backup_time(synced_backup_time); 87 } 88 89 // Store the synced device info for the local device only 90 // it is the same as the local info. Otherwise store the local 91 // device info and issue a change further below after finishing 92 // processing the |initial_sync_data|. 93 if (synced_local_device_info->Equals(*local_device_info) && 94 synced_backup_time == local_device_backup_time()) { 95 change_type = SyncChange::ACTION_INVALID; 96 } else { 97 num_items_updated++; 98 change_type = SyncChange::ACTION_UPDATE; 99 continue; 100 } 101 } else { 102 // A new device that doesn't match the local device. 103 num_items_new++; 104 } 105 106 StoreSyncData(id, *iter); 107 } 108 109 syncer::SyncMergeResult result(type); 110 111 // Add SyncData for the local device if it is new or different than 112 // the synced one, and also add it to the |change_list|. 113 if (change_type != SyncChange::ACTION_INVALID) { 114 SyncData local_data = CreateLocalData(local_device_info); 115 StoreSyncData(local_device_info->guid(), local_data); 116 117 SyncChangeList change_list; 118 change_list.push_back(SyncChange(FROM_HERE, change_type, local_data)); 119 result.set_error( 120 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list)); 121 } 122 123 result.set_num_items_before_association(1); 124 result.set_num_items_after_association(all_data_.size()); 125 result.set_num_items_added(num_items_new); 126 result.set_num_items_modified(num_items_updated); 127 result.set_num_items_deleted(0); 128 129 NotifyObservers(); 130 131 return result; 132} 133 134void DeviceInfoSyncService::StopSyncing(syncer::ModelType type) { 135 all_data_.clear(); 136 sync_processor_.reset(); 137 error_handler_.reset(); 138 clear_local_device_backup_time(); 139} 140 141SyncDataList DeviceInfoSyncService::GetAllSyncData( 142 syncer::ModelType type) const { 143 SyncDataList list; 144 145 for (SyncDataMap::const_iterator iter = all_data_.begin(); 146 iter != all_data_.end(); 147 ++iter) { 148 list.push_back(iter->second); 149 } 150 151 return list; 152} 153 154syncer::SyncError DeviceInfoSyncService::ProcessSyncChanges( 155 const tracked_objects::Location& from_here, 156 const SyncChangeList& change_list) { 157 syncer::SyncError error; 158 159 DCHECK(local_device_info_provider_->GetLocalDeviceInfo()); 160 const std::string& local_device_id = 161 local_device_info_provider_->GetLocalDeviceInfo()->guid(); 162 163 bool has_changes = false; 164 165 // Iterate over all chanages and merge entries. 166 for (SyncChangeList::const_iterator iter = change_list.begin(); 167 iter != change_list.end(); 168 ++iter) { 169 const SyncData& sync_data = iter->sync_data(); 170 DCHECK_EQ(syncer::DEVICE_INFO, sync_data.GetDataType()); 171 172 const std::string& client_id = 173 sync_data.GetSpecifics().device_info().cache_guid(); 174 // Ignore device info matching the local device. 175 if (local_device_id == client_id) { 176 DVLOG(1) << "Ignoring sync changes for the local DEVICE_INFO"; 177 continue; 178 } 179 180 if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) { 181 has_changes = true; 182 DeleteSyncData(client_id); 183 } else if (iter->change_type() == syncer::SyncChange::ACTION_UPDATE || 184 iter->change_type() == syncer::SyncChange::ACTION_ADD) { 185 has_changes = true; 186 StoreSyncData(client_id, sync_data); 187 } else { 188 error.Reset(FROM_HERE, "Invalid action received.", syncer::DEVICE_INFO); 189 } 190 } 191 192 if (has_changes) { 193 NotifyObservers(); 194 } 195 196 return error; 197} 198 199scoped_ptr<DeviceInfo> DeviceInfoSyncService::GetDeviceInfo( 200 const std::string& client_id) const { 201 SyncDataMap::const_iterator iter = all_data_.find(client_id); 202 if (iter == all_data_.end()) { 203 return scoped_ptr<DeviceInfo>(); 204 } 205 206 return make_scoped_ptr(CreateDeviceInfo(iter->second)); 207} 208 209ScopedVector<DeviceInfo> DeviceInfoSyncService::GetAllDeviceInfo() const { 210 ScopedVector<DeviceInfo> list; 211 212 for (SyncDataMap::const_iterator iter = all_data_.begin(); 213 iter != all_data_.end(); 214 ++iter) { 215 list.push_back(CreateDeviceInfo(iter->second)); 216 } 217 218 return list.Pass(); 219} 220 221void DeviceInfoSyncService::AddObserver(Observer* observer) { 222 observers_.AddObserver(observer); 223} 224 225void DeviceInfoSyncService::RemoveObserver(Observer* observer) { 226 observers_.RemoveObserver(observer); 227} 228 229void DeviceInfoSyncService::NotifyObservers() { 230 FOR_EACH_OBSERVER(Observer, observers_, OnDeviceInfoChange()); 231} 232 233void DeviceInfoSyncService::UpdateLocalDeviceBackupTime( 234 base::Time backup_time) { 235 set_local_device_backup_time(syncer::TimeToProtoTime(backup_time)); 236 237 if (sync_processor_.get()) { 238 // Local device info must be available in advance 239 DCHECK(local_device_info_provider_->GetLocalDeviceInfo()); 240 const std::string& local_id = 241 local_device_info_provider_->GetLocalDeviceInfo()->guid(); 242 243 SyncDataMap::iterator iter = all_data_.find(local_id); 244 DCHECK(iter != all_data_.end()); 245 246 syncer::SyncData& data = iter->second; 247 if (UpdateBackupTime(&data)) { 248 // Local device backup time has changed. 249 // Push changes to the server via the |sync_processor_|. 250 SyncChangeList change_list; 251 change_list.push_back(SyncChange( 252 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, data)); 253 sync_processor_->ProcessSyncChanges(FROM_HERE, change_list); 254 } 255 } 256} 257 258bool DeviceInfoSyncService::UpdateBackupTime(syncer::SyncData* sync_data) { 259 DCHECK(has_local_device_backup_time()); 260 DCHECK(sync_data->GetSpecifics().has_device_info()); 261 const sync_pb::DeviceInfoSpecifics& source_specifics = 262 sync_data->GetSpecifics().device_info(); 263 264 if (!source_specifics.has_backup_timestamp() || 265 source_specifics.backup_timestamp() != local_device_backup_time()) { 266 sync_pb::EntitySpecifics entity(sync_data->GetSpecifics()); 267 entity.mutable_device_info()->set_backup_timestamp( 268 local_device_backup_time()); 269 *sync_data = CreateLocalData(entity); 270 271 return true; 272 } 273 274 return false; 275} 276 277base::Time DeviceInfoSyncService::GetLocalDeviceBackupTime() const { 278 return has_local_device_backup_time() 279 ? syncer::ProtoTimeToTime(local_device_backup_time()) 280 : base::Time(); 281} 282 283SyncData DeviceInfoSyncService::CreateLocalData(const DeviceInfo* info) { 284 sync_pb::EntitySpecifics entity; 285 sync_pb::DeviceInfoSpecifics& specifics = *entity.mutable_device_info(); 286 287 specifics.set_cache_guid(info->guid()); 288 specifics.set_client_name(info->client_name()); 289 specifics.set_chrome_version(info->chrome_version()); 290 specifics.set_sync_user_agent(info->sync_user_agent()); 291 specifics.set_device_type(info->device_type()); 292 specifics.set_signin_scoped_device_id(info->signin_scoped_device_id()); 293 294 if (has_local_device_backup_time()) { 295 specifics.set_backup_timestamp(local_device_backup_time()); 296 } 297 298 return CreateLocalData(entity); 299} 300 301SyncData DeviceInfoSyncService::CreateLocalData( 302 const sync_pb::EntitySpecifics& entity) { 303 const sync_pb::DeviceInfoSpecifics& specifics = entity.device_info(); 304 305 std::string local_device_tag = 306 base::StringPrintf("DeviceInfo_%s", specifics.cache_guid().c_str()); 307 308 return SyncData::CreateLocalData( 309 local_device_tag, specifics.client_name(), entity); 310} 311 312DeviceInfo* DeviceInfoSyncService::CreateDeviceInfo( 313 const syncer::SyncData sync_data) { 314 const sync_pb::DeviceInfoSpecifics& specifics = 315 sync_data.GetSpecifics().device_info(); 316 317 return new DeviceInfo(specifics.cache_guid(), 318 specifics.client_name(), 319 specifics.chrome_version(), 320 specifics.sync_user_agent(), 321 specifics.device_type(), 322 specifics.signin_scoped_device_id()); 323} 324 325void DeviceInfoSyncService::StoreSyncData(const std::string& client_id, 326 const SyncData& sync_data) { 327 DVLOG(1) << "Storing DEVICE_INFO for " 328 << sync_data.GetSpecifics().device_info().client_name() 329 << " with ID " << client_id; 330 all_data_[client_id] = sync_data; 331} 332 333void DeviceInfoSyncService::DeleteSyncData(const std::string& client_id) { 334 SyncDataMap::iterator iter = all_data_.find(client_id); 335 if (iter != all_data_.end()) { 336 DVLOG(1) << "Deleting DEVICE_INFO for " 337 << iter->second.GetSpecifics().device_info().client_name() 338 << " with ID " << client_id; 339 all_data_.erase(iter); 340 } 341} 342 343} // namespace sync_driver 344