| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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/device_info/device_info_service.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <set> | |
| 11 #include <utility> | |
| 12 | |
| 13 #include "base/bind.h" | |
| 14 #include "base/location.h" | |
| 15 #include "base/memory/ptr_util.h" | |
| 16 #include "base/strings/string_util.h" | |
| 17 #include "components/sync/base/time.h" | |
| 18 #include "components/sync/device_info/device_info_util.h" | |
| 19 #include "components/sync/model/entity_change.h" | |
| 20 #include "components/sync/model/metadata_batch.h" | |
| 21 #include "components/sync/model/mutable_data_batch.h" | |
| 22 #include "components/sync/model/sync_error.h" | |
| 23 #include "components/sync/protocol/model_type_state.pb.h" | |
| 24 #include "components/sync/protocol/sync.pb.h" | |
| 25 | |
| 26 namespace syncer { | |
| 27 | |
| 28 using base::Time; | |
| 29 using base::TimeDelta; | |
| 30 using sync_pb::DeviceInfoSpecifics; | |
| 31 using sync_pb::EntitySpecifics; | |
| 32 using sync_pb::ModelTypeState; | |
| 33 | |
| 34 using Record = ModelTypeStore::Record; | |
| 35 using RecordList = ModelTypeStore::RecordList; | |
| 36 using Result = ModelTypeStore::Result; | |
| 37 using WriteBatch = ModelTypeStore::WriteBatch; | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 // Find the timestamp for the last time this |device_info| was edited. | |
| 42 Time GetLastUpdateTime(const DeviceInfoSpecifics& specifics) { | |
| 43 if (specifics.has_last_updated_timestamp()) { | |
| 44 return ProtoTimeToTime(specifics.last_updated_timestamp()); | |
| 45 } else { | |
| 46 return Time(); | |
| 47 } | |
| 48 } | |
| 49 | |
| 50 // Converts DeviceInfoSpecifics into a freshly allocated DeviceInfo. | |
| 51 std::unique_ptr<DeviceInfo> SpecificsToModel( | |
| 52 const DeviceInfoSpecifics& specifics) { | |
| 53 return base::MakeUnique<DeviceInfo>( | |
| 54 specifics.cache_guid(), specifics.client_name(), | |
| 55 specifics.chrome_version(), specifics.sync_user_agent(), | |
| 56 specifics.device_type(), specifics.signin_scoped_device_id()); | |
| 57 } | |
| 58 | |
| 59 // Allocate a EntityData and copies |specifics| into it. | |
| 60 std::unique_ptr<EntityData> CopyToEntityData( | |
| 61 const DeviceInfoSpecifics& specifics) { | |
| 62 auto entity_data = base::MakeUnique<EntityData>(); | |
| 63 *entity_data->specifics.mutable_device_info() = specifics; | |
| 64 entity_data->non_unique_name = specifics.client_name(); | |
| 65 return entity_data; | |
| 66 } | |
| 67 | |
| 68 // Converts DeviceInfo into a freshly allocated DeviceInfoSpecifics. Takes | |
| 69 // |last_updated_timestamp| to set because the model object does not contain | |
| 70 // this concept. | |
| 71 std::unique_ptr<DeviceInfoSpecifics> ModelToSpecifics( | |
| 72 const DeviceInfo& info, | |
| 73 int64_t last_updated_timestamp) { | |
| 74 auto specifics = base::MakeUnique<DeviceInfoSpecifics>(); | |
| 75 specifics->set_cache_guid(info.guid()); | |
| 76 specifics->set_client_name(info.client_name()); | |
| 77 specifics->set_chrome_version(info.chrome_version()); | |
| 78 specifics->set_sync_user_agent(info.sync_user_agent()); | |
| 79 specifics->set_device_type(info.device_type()); | |
| 80 specifics->set_signin_scoped_device_id(info.signin_scoped_device_id()); | |
| 81 specifics->set_last_updated_timestamp(last_updated_timestamp); | |
| 82 return specifics; | |
| 83 } | |
| 84 | |
| 85 } // namespace | |
| 86 | |
| 87 DeviceInfoService::DeviceInfoService( | |
| 88 LocalDeviceInfoProvider* local_device_info_provider, | |
| 89 const StoreFactoryFunction& callback, | |
| 90 const ChangeProcessorFactory& change_processor_factory) | |
| 91 : ModelTypeSyncBridge(change_processor_factory, DEVICE_INFO), | |
| 92 local_device_info_provider_(local_device_info_provider) { | |
| 93 DCHECK(local_device_info_provider); | |
| 94 | |
| 95 // This is not threadsafe, but presuably the provider initializes on the same | |
| 96 // thread as us so we're okay. | |
| 97 if (local_device_info_provider->GetLocalDeviceInfo()) { | |
| 98 OnProviderInitialized(); | |
| 99 } else { | |
| 100 subscription_ = | |
| 101 local_device_info_provider->RegisterOnInitializedCallback(base::Bind( | |
| 102 &DeviceInfoService::OnProviderInitialized, base::Unretained(this))); | |
| 103 } | |
| 104 | |
| 105 callback.Run( | |
| 106 base::Bind(&DeviceInfoService::OnStoreCreated, base::AsWeakPtr(this))); | |
| 107 } | |
| 108 | |
| 109 DeviceInfoService::~DeviceInfoService() {} | |
| 110 | |
| 111 std::unique_ptr<MetadataChangeList> | |
| 112 DeviceInfoService::CreateMetadataChangeList() { | |
| 113 return base::MakeUnique<SimpleMetadataChangeList>(); | |
| 114 } | |
| 115 | |
| 116 SyncError DeviceInfoService::MergeSyncData( | |
| 117 std::unique_ptr<MetadataChangeList> metadata_change_list, | |
| 118 EntityDataMap entity_data_map) { | |
| 119 DCHECK(has_provider_initialized_); | |
| 120 DCHECK(change_processor()->IsTrackingMetadata()); | |
| 121 | |
| 122 // Local data should typically be near empty, with the only possible value | |
| 123 // corresponding to this device. This is because on signout all device info | |
| 124 // data is blown away. However, this simplification is being ignored here and | |
| 125 // a full difference is going to be calculated to explore what other service | |
| 126 // implementations may look like. | |
| 127 std::set<std::string> local_guids_to_put; | |
| 128 for (const auto& kv : all_data_) { | |
| 129 local_guids_to_put.insert(kv.first); | |
| 130 } | |
| 131 | |
| 132 bool has_changes = false; | |
| 133 const DeviceInfo* local_info = | |
| 134 local_device_info_provider_->GetLocalDeviceInfo(); | |
| 135 std::string local_guid = local_info->guid(); | |
| 136 std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch(); | |
| 137 for (const auto& kv : entity_data_map) { | |
| 138 const DeviceInfoSpecifics& specifics = | |
| 139 kv.second.value().specifics.device_info(); | |
| 140 DCHECK_EQ(kv.first, specifics.cache_guid()); | |
| 141 if (specifics.cache_guid() == local_guid) { | |
| 142 // Don't Put local data if it's the same as the remote copy. | |
| 143 if (local_info->Equals(*SpecificsToModel(specifics))) { | |
| 144 local_guids_to_put.erase(local_guid); | |
| 145 } else { | |
| 146 // This device is valid right now and this entry is about to be | |
| 147 // committed, use this as an opportunity to refresh the timestamp. | |
| 148 all_data_[local_guid]->set_last_updated_timestamp( | |
| 149 TimeToProtoTime(Time::Now())); | |
| 150 } | |
| 151 } else { | |
| 152 // Remote data wins conflicts. | |
| 153 local_guids_to_put.erase(specifics.cache_guid()); | |
| 154 has_changes = true; | |
| 155 StoreSpecifics(base::MakeUnique<DeviceInfoSpecifics>(specifics), | |
| 156 batch.get()); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 for (const std::string& guid : local_guids_to_put) { | |
| 161 change_processor()->Put(guid, CopyToEntityData(*all_data_[guid]), | |
| 162 metadata_change_list.get()); | |
| 163 } | |
| 164 | |
| 165 CommitAndNotify(std::move(batch), std::move(metadata_change_list), | |
| 166 has_changes); | |
| 167 return SyncError(); | |
| 168 } | |
| 169 | |
| 170 SyncError DeviceInfoService::ApplySyncChanges( | |
| 171 std::unique_ptr<MetadataChangeList> metadata_change_list, | |
| 172 EntityChangeList entity_changes) { | |
| 173 DCHECK(has_provider_initialized_); | |
| 174 | |
| 175 std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch(); | |
| 176 bool has_changes = false; | |
| 177 for (EntityChange& change : entity_changes) { | |
| 178 const std::string guid = change.storage_key(); | |
| 179 // Each device is the authoritative source for itself, ignore any remote | |
| 180 // changes that have our local cache guid. | |
| 181 if (guid == local_device_info_provider_->GetLocalDeviceInfo()->guid()) { | |
| 182 continue; | |
| 183 } | |
| 184 | |
| 185 if (change.type() == EntityChange::ACTION_DELETE) { | |
| 186 has_changes |= DeleteSpecifics(guid, batch.get()); | |
| 187 } else { | |
| 188 const DeviceInfoSpecifics& specifics = | |
| 189 change.data().specifics.device_info(); | |
| 190 DCHECK(guid == specifics.cache_guid()); | |
| 191 StoreSpecifics(base::MakeUnique<DeviceInfoSpecifics>(specifics), | |
| 192 batch.get()); | |
| 193 has_changes = true; | |
| 194 } | |
| 195 } | |
| 196 | |
| 197 CommitAndNotify(std::move(batch), std::move(metadata_change_list), | |
| 198 has_changes); | |
| 199 return SyncError(); | |
| 200 } | |
| 201 | |
| 202 void DeviceInfoService::GetData(StorageKeyList storage_keys, | |
| 203 DataCallback callback) { | |
| 204 auto batch = base::MakeUnique<MutableDataBatch>(); | |
| 205 for (const auto& key : storage_keys) { | |
| 206 const auto& iter = all_data_.find(key); | |
| 207 if (iter != all_data_.end()) { | |
| 208 DCHECK_EQ(key, iter->second->cache_guid()); | |
| 209 batch->Put(key, CopyToEntityData(*iter->second)); | |
| 210 } | |
| 211 } | |
| 212 callback.Run(SyncError(), std::move(batch)); | |
| 213 } | |
| 214 | |
| 215 void DeviceInfoService::GetAllData(DataCallback callback) { | |
| 216 auto batch = base::MakeUnique<MutableDataBatch>(); | |
| 217 for (const auto& kv : all_data_) { | |
| 218 batch->Put(kv.first, CopyToEntityData(*kv.second)); | |
| 219 } | |
| 220 callback.Run(SyncError(), std::move(batch)); | |
| 221 } | |
| 222 | |
| 223 std::string DeviceInfoService::GetClientTag(const EntityData& entity_data) { | |
| 224 DCHECK(entity_data.specifics.has_device_info()); | |
| 225 return DeviceInfoUtil::SpecificsToTag(entity_data.specifics.device_info()); | |
| 226 } | |
| 227 | |
| 228 std::string DeviceInfoService::GetStorageKey(const EntityData& entity_data) { | |
| 229 DCHECK(entity_data.specifics.has_device_info()); | |
| 230 return entity_data.specifics.device_info().cache_guid(); | |
| 231 } | |
| 232 | |
| 233 void DeviceInfoService::DisableSync() { | |
| 234 // TODO(skym, crbug.com/659263): Would it be reasonable to pulse_timer_.Stop() | |
| 235 // or subscription_.reset() here? | |
| 236 | |
| 237 // Allow deletion of metadata to happen before the deletion of data below. If | |
| 238 // we crash after removing metadata but not regular data, then merge can | |
| 239 // handle pairing everything back up. | |
| 240 ModelTypeSyncBridge::DisableSync(); | |
| 241 | |
| 242 // Remove all local data, if sync is being disabled, the user has expressed | |
| 243 // their desire to not have knowledge about other devices. | |
| 244 if (!all_data_.empty()) { | |
| 245 std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch(); | |
| 246 for (const auto& kv : all_data_) { | |
| 247 store_->DeleteData(batch.get(), kv.first); | |
| 248 } | |
| 249 store_->CommitWriteBatch( | |
| 250 std::move(batch), | |
| 251 base::Bind(&DeviceInfoService::OnCommit, base::AsWeakPtr(this))); | |
| 252 | |
| 253 all_data_.clear(); | |
| 254 NotifyObservers(); | |
| 255 } | |
| 256 } | |
| 257 | |
| 258 bool DeviceInfoService::IsSyncing() const { | |
| 259 return !all_data_.empty(); | |
| 260 } | |
| 261 | |
| 262 std::unique_ptr<DeviceInfo> DeviceInfoService::GetDeviceInfo( | |
| 263 const std::string& client_id) const { | |
| 264 const ClientIdToSpecifics::const_iterator iter = all_data_.find(client_id); | |
| 265 if (iter == all_data_.end()) { | |
| 266 return std::unique_ptr<DeviceInfo>(); | |
| 267 } | |
| 268 return SpecificsToModel(*iter->second); | |
| 269 } | |
| 270 | |
| 271 std::vector<std::unique_ptr<DeviceInfo>> DeviceInfoService::GetAllDeviceInfo() | |
| 272 const { | |
| 273 std::vector<std::unique_ptr<DeviceInfo>> list; | |
| 274 for (ClientIdToSpecifics::const_iterator iter = all_data_.begin(); | |
| 275 iter != all_data_.end(); ++iter) { | |
| 276 list.push_back(SpecificsToModel(*iter->second)); | |
| 277 } | |
| 278 return list; | |
| 279 } | |
| 280 | |
| 281 void DeviceInfoService::AddObserver(Observer* observer) { | |
| 282 observers_.AddObserver(observer); | |
| 283 } | |
| 284 | |
| 285 void DeviceInfoService::RemoveObserver(Observer* observer) { | |
| 286 observers_.RemoveObserver(observer); | |
| 287 } | |
| 288 | |
| 289 int DeviceInfoService::CountActiveDevices() const { | |
| 290 return CountActiveDevices(Time::Now()); | |
| 291 } | |
| 292 | |
| 293 void DeviceInfoService::NotifyObservers() { | |
| 294 for (auto& observer : observers_) | |
| 295 observer.OnDeviceInfoChange(); | |
| 296 } | |
| 297 | |
| 298 void DeviceInfoService::StoreSpecifics( | |
| 299 std::unique_ptr<DeviceInfoSpecifics> specifics, | |
| 300 WriteBatch* batch) { | |
| 301 const std::string guid = specifics->cache_guid(); | |
| 302 store_->WriteData(batch, guid, specifics->SerializeAsString()); | |
| 303 all_data_[guid] = std::move(specifics); | |
| 304 } | |
| 305 | |
| 306 bool DeviceInfoService::DeleteSpecifics(const std::string& guid, | |
| 307 WriteBatch* batch) { | |
| 308 ClientIdToSpecifics::const_iterator iter = all_data_.find(guid); | |
| 309 if (iter != all_data_.end()) { | |
| 310 store_->DeleteData(batch, guid); | |
| 311 all_data_.erase(iter); | |
| 312 return true; | |
| 313 } else { | |
| 314 return false; | |
| 315 } | |
| 316 } | |
| 317 | |
| 318 void DeviceInfoService::OnProviderInitialized() { | |
| 319 // Now that the provider has initialized, remove the subscription. The service | |
| 320 // should only need to give the processor metadata upon initialization. If | |
| 321 // sync is disabled and enabled, our provider will try to retrigger this | |
| 322 // event, but we do not want to send any more metadata to the processor. | |
| 323 subscription_.reset(); | |
| 324 | |
| 325 has_provider_initialized_ = true; | |
| 326 LoadMetadataIfReady(); | |
| 327 } | |
| 328 | |
| 329 void DeviceInfoService::OnStoreCreated(Result result, | |
| 330 std::unique_ptr<ModelTypeStore> store) { | |
| 331 if (result == Result::SUCCESS) { | |
| 332 std::swap(store_, store); | |
| 333 store_->ReadAllData( | |
| 334 base::Bind(&DeviceInfoService::OnReadAllData, base::AsWeakPtr(this))); | |
| 335 } else { | |
| 336 ReportStartupErrorToSync("ModelTypeStore creation failed."); | |
| 337 } | |
| 338 } | |
| 339 | |
| 340 void DeviceInfoService::OnReadAllData(Result result, | |
| 341 std::unique_ptr<RecordList> record_list) { | |
| 342 if (result != Result::SUCCESS) { | |
| 343 ReportStartupErrorToSync("Initial load of data failed."); | |
| 344 return; | |
| 345 } | |
| 346 | |
| 347 for (const Record& r : *record_list.get()) { | |
| 348 std::unique_ptr<DeviceInfoSpecifics> specifics = | |
| 349 base::MakeUnique<DeviceInfoSpecifics>(); | |
| 350 if (specifics->ParseFromString(r.value)) { | |
| 351 all_data_[specifics->cache_guid()] = std::move(specifics); | |
| 352 } else { | |
| 353 ReportStartupErrorToSync("Failed to deserialize specifics."); | |
| 354 } | |
| 355 } | |
| 356 | |
| 357 has_data_loaded_ = true; | |
| 358 LoadMetadataIfReady(); | |
| 359 } | |
| 360 | |
| 361 void DeviceInfoService::LoadMetadataIfReady() { | |
| 362 if (has_data_loaded_ && has_provider_initialized_) { | |
| 363 store_->ReadAllMetadata(base::Bind(&DeviceInfoService::OnReadAllMetadata, | |
| 364 base::AsWeakPtr(this))); | |
| 365 } | |
| 366 } | |
| 367 | |
| 368 void DeviceInfoService::OnReadAllMetadata( | |
| 369 Result result, | |
| 370 std::unique_ptr<RecordList> metadata_records, | |
| 371 const std::string& global_metadata) { | |
| 372 if (result != Result::SUCCESS) { | |
| 373 ReportStartupErrorToSync("Load of metadata completely failed."); | |
| 374 return; | |
| 375 } | |
| 376 | |
| 377 auto batch = base::MakeUnique<MetadataBatch>(); | |
| 378 ModelTypeState state; | |
| 379 if (state.ParseFromString(global_metadata)) { | |
| 380 batch->SetModelTypeState(state); | |
| 381 } else { | |
| 382 ReportStartupErrorToSync("Failed to deserialize global metadata."); | |
| 383 return; | |
| 384 } | |
| 385 | |
| 386 for (const Record& r : *metadata_records.get()) { | |
| 387 sync_pb::EntityMetadata entity_metadata; | |
| 388 if (entity_metadata.ParseFromString(r.value)) { | |
| 389 batch->AddMetadata(r.id, entity_metadata); | |
| 390 } else { | |
| 391 ReportStartupErrorToSync("Failed to deserialize entity metadata."); | |
| 392 } | |
| 393 } | |
| 394 | |
| 395 change_processor()->OnMetadataLoaded(SyncError(), std::move(batch)); | |
| 396 ReconcileLocalAndStored(); | |
| 397 } | |
| 398 | |
| 399 void DeviceInfoService::OnCommit(Result result) { | |
| 400 if (result != Result::SUCCESS) { | |
| 401 change_processor()->CreateAndUploadError(FROM_HERE, | |
| 402 "Failed a write to store."); | |
| 403 } | |
| 404 } | |
| 405 | |
| 406 void DeviceInfoService::ReconcileLocalAndStored() { | |
| 407 // On initial syncing we will have a change processor here, but it will not be | |
| 408 // tracking changes. We need to persist a copy of our local device info to | |
| 409 // disk, but the Put call to the processor will be ignored. That should be | |
| 410 // fine however, as the discrepancy will be picked up later in merge. We don't | |
| 411 // bother trying to track this case and act intelligently because simply not | |
| 412 // much of a benefit in doing so. | |
| 413 DCHECK(has_provider_initialized_); | |
| 414 | |
| 415 const DeviceInfo* current_info = | |
| 416 local_device_info_provider_->GetLocalDeviceInfo(); | |
| 417 auto iter = all_data_.find(current_info->guid()); | |
| 418 | |
| 419 // Convert to DeviceInfo for Equals function. | |
| 420 if (iter != all_data_.end() && | |
| 421 current_info->Equals(*SpecificsToModel(*iter->second))) { | |
| 422 const TimeDelta pulse_delay(DeviceInfoUtil::CalculatePulseDelay( | |
| 423 GetLastUpdateTime(*iter->second), Time::Now())); | |
| 424 if (!pulse_delay.is_zero()) { | |
| 425 pulse_timer_.Start(FROM_HERE, pulse_delay, | |
| 426 base::Bind(&DeviceInfoService::SendLocalData, | |
| 427 base::Unretained(this))); | |
| 428 return; | |
| 429 } | |
| 430 } | |
| 431 SendLocalData(); | |
| 432 } | |
| 433 | |
| 434 void DeviceInfoService::SendLocalData() { | |
| 435 DCHECK(has_provider_initialized_); | |
| 436 | |
| 437 // It is possible that the provider no longer has data for us, such as when | |
| 438 // the user signs out. No-op this pulse, but keep the timer going in case sync | |
| 439 // is enabled later. | |
| 440 if (local_device_info_provider_->GetLocalDeviceInfo() != nullptr) { | |
| 441 std::unique_ptr<DeviceInfoSpecifics> specifics = | |
| 442 ModelToSpecifics(*local_device_info_provider_->GetLocalDeviceInfo(), | |
| 443 TimeToProtoTime(Time::Now())); | |
| 444 std::unique_ptr<MetadataChangeList> metadata_change_list = | |
| 445 CreateMetadataChangeList(); | |
| 446 if (change_processor()->IsTrackingMetadata()) { | |
| 447 change_processor()->Put(specifics->cache_guid(), | |
| 448 CopyToEntityData(*specifics), | |
| 449 metadata_change_list.get()); | |
| 450 } | |
| 451 | |
| 452 std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch(); | |
| 453 StoreSpecifics(std::move(specifics), batch.get()); | |
| 454 CommitAndNotify(std::move(batch), std::move(metadata_change_list), true); | |
| 455 } | |
| 456 | |
| 457 pulse_timer_.Start( | |
| 458 FROM_HERE, DeviceInfoUtil::kPulseInterval, | |
| 459 base::Bind(&DeviceInfoService::SendLocalData, base::Unretained(this))); | |
| 460 } | |
| 461 | |
| 462 void DeviceInfoService::CommitAndNotify( | |
| 463 std::unique_ptr<WriteBatch> batch, | |
| 464 std::unique_ptr<MetadataChangeList> metadata_change_list, | |
| 465 bool should_notify) { | |
| 466 static_cast<SimpleMetadataChangeList*>(metadata_change_list.get()) | |
| 467 ->TransferChanges(store_.get(), batch.get()); | |
| 468 store_->CommitWriteBatch( | |
| 469 std::move(batch), | |
| 470 base::Bind(&DeviceInfoService::OnCommit, base::AsWeakPtr(this))); | |
| 471 if (should_notify) { | |
| 472 NotifyObservers(); | |
| 473 } | |
| 474 } | |
| 475 | |
| 476 int DeviceInfoService::CountActiveDevices(const Time now) const { | |
| 477 return std::count_if(all_data_.begin(), all_data_.end(), | |
| 478 [now](ClientIdToSpecifics::const_reference pair) { | |
| 479 return DeviceInfoUtil::IsActive( | |
| 480 GetLastUpdateTime(*pair.second), now); | |
| 481 }); | |
| 482 } | |
| 483 | |
| 484 void DeviceInfoService::ReportStartupErrorToSync(const std::string& msg) { | |
| 485 // TODO(skym): Shouldn't need to log this here, reporting should always log. | |
| 486 LOG(WARNING) << msg; | |
| 487 change_processor()->OnMetadataLoaded( | |
| 488 change_processor()->CreateAndUploadError(FROM_HERE, msg), nullptr); | |
| 489 } | |
| 490 | |
| 491 } // namespace syncer | |
| OLD | NEW |