Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(175)

Side by Side Diff: components/sync_driver/device_info_service.cc

Issue 2203673002: [Sync] Move //components/sync_driver to //components/sync/driver. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@sd-a
Patch Set: Full change rebased on static lib. Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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_driver/device_info_service.h"
6
7 #include <set>
8 #include <utility>
9 #include <vector>
10
11 #include "base/bind.h"
12 #include "base/location.h"
13 #include "base/memory/ptr_util.h"
14 #include "base/strings/string_util.h"
15 #include "components/sync/api/entity_change.h"
16 #include "components/sync/api/metadata_batch.h"
17 #include "components/sync/api/sync_error.h"
18 #include "components/sync/base/time.h"
19 #include "components/sync/core/data_batch_impl.h"
20 #include "components/sync/core/simple_metadata_change_list.h"
21 #include "components/sync/protocol/data_type_state.pb.h"
22 #include "components/sync/protocol/sync.pb.h"
23 #include "components/sync_driver/device_info_util.h"
24
25 namespace sync_driver_v2 {
26
27 using base::Time;
28 using base::TimeDelta;
29 using syncer::SyncError;
30 using syncer_v2::DataBatchImpl;
31 using syncer_v2::EntityChange;
32 using syncer_v2::EntityChangeList;
33 using syncer_v2::EntityData;
34 using syncer_v2::EntityDataMap;
35 using syncer_v2::MetadataBatch;
36 using syncer_v2::MetadataChangeList;
37 using syncer_v2::ModelTypeStore;
38 using syncer_v2::SimpleMetadataChangeList;
39 using sync_driver::DeviceInfo;
40 using sync_driver::DeviceInfoUtil;
41 using sync_pb::DataTypeState;
42 using sync_pb::DeviceInfoSpecifics;
43 using sync_pb::EntitySpecifics;
44
45 using Record = ModelTypeStore::Record;
46 using RecordList = ModelTypeStore::RecordList;
47 using Result = ModelTypeStore::Result;
48 using WriteBatch = ModelTypeStore::WriteBatch;
49
50 DeviceInfoService::DeviceInfoService(
51 sync_driver::LocalDeviceInfoProvider* local_device_info_provider,
52 const StoreFactoryFunction& callback,
53 const ChangeProcessorFactory& change_processor_factory)
54 : ModelTypeService(change_processor_factory, syncer::DEVICE_INFO),
55 local_device_info_provider_(local_device_info_provider),
56 weak_factory_(this) {
57 DCHECK(local_device_info_provider);
58
59 // This is not threadsafe, but presuably the provider initializes on the same
60 // thread as us so we're okay.
61 if (local_device_info_provider->GetLocalDeviceInfo()) {
62 OnProviderInitialized();
63 } else {
64 subscription_ =
65 local_device_info_provider->RegisterOnInitializedCallback(base::Bind(
66 &DeviceInfoService::OnProviderInitialized, base::Unretained(this)));
67 }
68
69 callback.Run(base::Bind(&DeviceInfoService::OnStoreCreated,
70 weak_factory_.GetWeakPtr()));
71 }
72
73 DeviceInfoService::~DeviceInfoService() {}
74
75 std::unique_ptr<MetadataChangeList>
76 DeviceInfoService::CreateMetadataChangeList() {
77 return base::WrapUnique(new SimpleMetadataChangeList());
78 }
79
80 SyncError DeviceInfoService::MergeSyncData(
81 std::unique_ptr<MetadataChangeList> metadata_change_list,
82 EntityDataMap entity_data_map) {
83 DCHECK(has_provider_initialized_);
84 DCHECK(has_metadata_loaded_);
85 DCHECK(change_processor());
86
87 // Local data should typically be near empty, with the only possible value
88 // corresponding to this device. This is because on signout all device info
89 // data is blown away. However, this simplification is being ignored here and
90 // a full difference is going to be calculated to explore what other service
91 // implementations may look like.
92 std::set<std::string> local_guids_to_put;
93 for (const auto& kv : all_data_) {
94 local_guids_to_put.insert(kv.first);
95 }
96
97 bool has_changes = false;
98 const DeviceInfo* local_info =
99 local_device_info_provider_->GetLocalDeviceInfo();
100 std::string local_guid = local_info->guid();
101 std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
102 for (const auto& kv : entity_data_map) {
103 const DeviceInfoSpecifics& specifics =
104 kv.second.value().specifics.device_info();
105 DCHECK_EQ(kv.first, specifics.cache_guid());
106 if (specifics.cache_guid() == local_guid) {
107 // Don't Put local data if it's the same as the remote copy.
108 if (local_info->Equals(*CopyToModel(specifics))) {
109 local_guids_to_put.erase(local_guid);
110 } else {
111 // This device is valid right now and this entry is about to be
112 // committed, use this as an opportunity to refresh the timestamp.
113 all_data_[local_guid]->set_last_updated_timestamp(
114 syncer::TimeToProtoTime(Time::Now()));
115 }
116 } else {
117 // Remote data wins conflicts.
118 local_guids_to_put.erase(specifics.cache_guid());
119 has_changes = true;
120 StoreSpecifics(base::WrapUnique(new DeviceInfoSpecifics(specifics)),
121 batch.get());
122 }
123 }
124
125 for (const std::string& guid : local_guids_to_put) {
126 change_processor()->Put(guid, CopyToEntityData(*all_data_[guid]),
127 metadata_change_list.get());
128 }
129
130 CommitAndNotify(std::move(batch), std::move(metadata_change_list),
131 has_changes);
132 return SyncError();
133 }
134
135 SyncError DeviceInfoService::ApplySyncChanges(
136 std::unique_ptr<MetadataChangeList> metadata_change_list,
137 EntityChangeList entity_changes) {
138 DCHECK(has_provider_initialized_);
139 DCHECK(has_metadata_loaded_);
140
141 std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
142 bool has_changes = false;
143 for (EntityChange& change : entity_changes) {
144 const std::string guid = change.storage_key();
145 // Each device is the authoritative source for itself, ignore any remote
146 // changes that have our local cache guid.
147 if (guid == local_device_info_provider_->GetLocalDeviceInfo()->guid()) {
148 continue;
149 }
150
151 if (change.type() == EntityChange::ACTION_DELETE) {
152 has_changes |= DeleteSpecifics(guid, batch.get());
153 } else {
154 const DeviceInfoSpecifics& specifics =
155 change.data().specifics.device_info();
156 DCHECK(guid == specifics.cache_guid());
157 StoreSpecifics(base::WrapUnique(new DeviceInfoSpecifics(specifics)),
158 batch.get());
159 has_changes = true;
160 }
161 }
162
163 CommitAndNotify(std::move(batch), std::move(metadata_change_list),
164 has_changes);
165 return SyncError();
166 }
167
168 void DeviceInfoService::GetData(StorageKeyList storage_keys,
169 DataCallback callback) {
170 DCHECK(has_metadata_loaded_);
171
172 std::unique_ptr<DataBatchImpl> batch(new DataBatchImpl());
173 for (const auto& key : storage_keys) {
174 const auto& iter = all_data_.find(key);
175 if (iter != all_data_.end()) {
176 DCHECK_EQ(key, iter->second->cache_guid());
177 batch->Put(key, CopyToEntityData(*iter->second));
178 }
179 }
180
181 callback.Run(SyncError(), std::move(batch));
182 }
183
184 void DeviceInfoService::GetAllData(DataCallback callback) {
185 DCHECK(has_metadata_loaded_);
186
187 std::unique_ptr<DataBatchImpl> batch(new DataBatchImpl());
188 for (const auto& kv : all_data_) {
189 batch->Put(kv.first, CopyToEntityData(*kv.second));
190 }
191
192 callback.Run(SyncError(), std::move(batch));
193 }
194
195 std::string DeviceInfoService::GetClientTag(const EntityData& entity_data) {
196 DCHECK(entity_data.specifics.has_device_info());
197 return DeviceInfoUtil::SpecificsToTag(entity_data.specifics.device_info());
198 }
199
200 std::string DeviceInfoService::GetStorageKey(
201 const syncer_v2::EntityData& entity_data) {
202 DCHECK(entity_data.specifics.has_device_info());
203 return entity_data.specifics.device_info().cache_guid();
204 }
205
206 void DeviceInfoService::OnChangeProcessorSet() {
207 // We've recieved a new processor that needs metadata. If we're still in the
208 // process of loading data and/or metadata, then |has_metadata_loaded_| is
209 // false and we'll wait for those async reads to happen. If we've already
210 // loaded metadata and then subsequently we get a new processor, we must not
211 // have created the processor ourselves because we had no metadata. So there
212 // must not be any metadata on disk.
213 if (has_metadata_loaded_) {
214 change_processor()->OnMetadataLoaded(SyncError(),
215 base::WrapUnique(new MetadataBatch()));
216 ReconcileLocalAndStored();
217 }
218 }
219
220 bool DeviceInfoService::IsSyncing() const {
221 return !all_data_.empty();
222 }
223
224 std::unique_ptr<DeviceInfo> DeviceInfoService::GetDeviceInfo(
225 const std::string& client_id) const {
226 const ClientIdToSpecifics::const_iterator iter = all_data_.find(client_id);
227 if (iter == all_data_.end()) {
228 return std::unique_ptr<DeviceInfo>();
229 }
230
231 return CopyToModel(*iter->second);
232 }
233
234 ScopedVector<DeviceInfo> DeviceInfoService::GetAllDeviceInfo() const {
235 ScopedVector<DeviceInfo> list;
236
237 for (ClientIdToSpecifics::const_iterator iter = all_data_.begin();
238 iter != all_data_.end(); ++iter) {
239 list.push_back(CopyToModel(*iter->second));
240 }
241
242 return list;
243 }
244
245 void DeviceInfoService::AddObserver(Observer* observer) {
246 observers_.AddObserver(observer);
247 }
248
249 void DeviceInfoService::RemoveObserver(Observer* observer) {
250 observers_.RemoveObserver(observer);
251 }
252
253 int DeviceInfoService::CountActiveDevices() const {
254 return CountActiveDevices(Time::Now());
255 }
256
257 void DeviceInfoService::NotifyObservers() {
258 FOR_EACH_OBSERVER(Observer, observers_, OnDeviceInfoChange());
259 }
260
261 // Static.
262 std::unique_ptr<DeviceInfoSpecifics> DeviceInfoService::CopyToSpecifics(
263 const DeviceInfo& info) {
264 std::unique_ptr<DeviceInfoSpecifics> specifics =
265 base::WrapUnique(new DeviceInfoSpecifics);
266 specifics->set_cache_guid(info.guid());
267 specifics->set_client_name(info.client_name());
268 specifics->set_chrome_version(info.chrome_version());
269 specifics->set_sync_user_agent(info.sync_user_agent());
270 specifics->set_device_type(info.device_type());
271 specifics->set_signin_scoped_device_id(info.signin_scoped_device_id());
272 return specifics;
273 }
274
275 // Static.
276 std::unique_ptr<DeviceInfo> DeviceInfoService::CopyToModel(
277 const DeviceInfoSpecifics& specifics) {
278 return base::WrapUnique(new DeviceInfo(
279 specifics.cache_guid(), specifics.client_name(),
280 specifics.chrome_version(), specifics.sync_user_agent(),
281 specifics.device_type(), specifics.signin_scoped_device_id()));
282 }
283
284 // Static.
285 std::unique_ptr<EntityData> DeviceInfoService::CopyToEntityData(
286 const DeviceInfoSpecifics& specifics) {
287 std::unique_ptr<EntityData> entity_data(new EntityData());
288 *entity_data->specifics.mutable_device_info() = specifics;
289 entity_data->non_unique_name = specifics.client_name();
290 return entity_data;
291 }
292
293 void DeviceInfoService::StoreSpecifics(
294 std::unique_ptr<DeviceInfoSpecifics> specifics,
295 WriteBatch* batch) {
296 const std::string guid = specifics->cache_guid();
297 DVLOG(1) << "Storing DEVICE_INFO for " << specifics->client_name()
298 << " with ID " << guid;
299 store_->WriteData(batch, guid, specifics->SerializeAsString());
300 all_data_[guid] = std::move(specifics);
301 }
302
303 bool DeviceInfoService::DeleteSpecifics(const std::string& guid,
304 WriteBatch* batch) {
305 ClientIdToSpecifics::const_iterator iter = all_data_.find(guid);
306 if (iter != all_data_.end()) {
307 DVLOG(1) << "Deleting DEVICE_INFO for " << iter->second->client_name()
308 << " with ID " << guid;
309 store_->DeleteData(batch, guid);
310 all_data_.erase(iter);
311 return true;
312 } else {
313 return false;
314 }
315 }
316
317 void DeviceInfoService::OnProviderInitialized() {
318 has_provider_initialized_ = true;
319 LoadMetadataIfReady();
320 }
321
322 void DeviceInfoService::OnStoreCreated(Result result,
323 std::unique_ptr<ModelTypeStore> store) {
324 if (result == Result::SUCCESS) {
325 std::swap(store_, store);
326 store_->ReadAllData(base::Bind(&DeviceInfoService::OnReadAllData,
327 weak_factory_.GetWeakPtr()));
328 } else {
329 ReportStartupErrorToSync("ModelTypeStore creation failed.");
330 // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
331 // failure.
332 }
333 }
334
335 void DeviceInfoService::OnReadAllData(Result result,
336 std::unique_ptr<RecordList> record_list) {
337 if (result != Result::SUCCESS) {
338 ReportStartupErrorToSync("Initial load of data failed.");
339 // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
340 // failure.
341 return;
342 }
343
344 for (const Record& r : *record_list.get()) {
345 std::unique_ptr<DeviceInfoSpecifics> specifics(
346 base::WrapUnique(new DeviceInfoSpecifics()));
347 if (specifics->ParseFromString(r.value)) {
348 all_data_[specifics->cache_guid()] = std::move(specifics);
349 } else {
350 ReportStartupErrorToSync("Failed to deserialize specifics.");
351 // TODO(skym, crbug.com/582460): Handle unrecoverable initialization
352 // failure.
353 }
354 }
355
356 has_data_loaded_ = true;
357 LoadMetadataIfReady();
358 }
359
360 void DeviceInfoService::LoadMetadataIfReady() {
361 if (has_data_loaded_ && has_provider_initialized_) {
362 store_->ReadAllMetadata(base::Bind(&DeviceInfoService::OnReadAllMetadata,
363 weak_factory_.GetWeakPtr()));
364 }
365 }
366
367 void DeviceInfoService::OnReadAllMetadata(
368 Result result,
369 std::unique_ptr<RecordList> metadata_records,
370 const std::string& global_metadata) {
371 DCHECK(!has_metadata_loaded_);
372
373 if (result != Result::SUCCESS) {
374 // Store has encountered some serious error. We should still be able to
375 // continue as a read only service, since if we got this far we must have
376 // loaded all data out succesfully.
377 ReportStartupErrorToSync("Load of metadata completely failed.");
378 return;
379 }
380
381 // If we have no metadata then we don't want to create a processor. The idea
382 // is that by not having a processor, the services will suffer less of a
383 // performance hit. This isn't terribly applicable for this model type, but
384 // we want this class to be as similar to other services as possible so follow
385 // the convention.
386 if (metadata_records->size() > 0 || !global_metadata.empty()) {
387 CreateChangeProcessor();
388 }
389
390 // Set this after OnChangeProcessorSet so that we can correctly avoid giving
391 // the processor empty metadata. We always want to set |has_metadata_loaded_|
392 // at this point so that we'll know to give a processor empty metadata if it
393 // is created later.
394 has_metadata_loaded_ = true;
395
396 if (!change_processor()) {
397 // This means we haven't been told to start syncing and we don't have any
398 // local metadata.
399 return;
400 }
401
402 std::unique_ptr<MetadataBatch> batch(new MetadataBatch());
403 DataTypeState state;
404 if (state.ParseFromString(global_metadata)) {
405 batch->SetDataTypeState(state);
406 } else {
407 // TODO(skym): How bad is this scenario? We may be able to just give an
408 // empty batch to the processor and we'll treat corrupted data type state
409 // as no data type state at all. The question is do we want to add any of
410 // the entity metadata to the batch or completely skip that step? We're
411 // going to have to perform a merge shortly. Does this decision/logic even
412 // belong in this service?
413 change_processor()->OnMetadataLoaded(
414 change_processor()->CreateAndUploadError(
415 FROM_HERE, "Failed to deserialize global metadata."),
416 nullptr);
417 }
418 for (const Record& r : *metadata_records.get()) {
419 sync_pb::EntityMetadata entity_metadata;
420 if (entity_metadata.ParseFromString(r.value)) {
421 batch->AddMetadata(r.id, entity_metadata);
422 } else {
423 // TODO(skym): This really isn't too bad. We just want to regenerate
424 // metadata for this particular entity. Unfortunately there isn't a
425 // convenient way to tell the processor to do this.
426 LOG(WARNING) << "Failed to deserialize entity metadata.";
427 }
428 }
429 change_processor()->OnMetadataLoaded(SyncError(), std::move(batch));
430 ReconcileLocalAndStored();
431 }
432
433 void DeviceInfoService::OnCommit(Result result) {
434 if (result != Result::SUCCESS) {
435 LOG(WARNING) << "Failed a write to store.";
436 }
437 }
438
439 void DeviceInfoService::ReconcileLocalAndStored() {
440 // On initial syncing we will have a change processor here, but it will not be
441 // tracking changes. We need to persist a copy of our local device info to
442 // disk, but the Put call to the processor will be ignored. That should be
443 // fine however, as the discrepancy will be picked up later in merge. We don't
444 // bother trying to track this case and act intelligently because simply not
445 // much of a benefit in doing so.
446 DCHECK(has_provider_initialized_);
447 DCHECK(has_metadata_loaded_);
448 DCHECK(change_processor());
449 const DeviceInfo* current_info =
450 local_device_info_provider_->GetLocalDeviceInfo();
451 auto iter = all_data_.find(current_info->guid());
452
453 // Convert to DeviceInfo for Equals function.
454 if (iter != all_data_.end() &&
455 current_info->Equals(*CopyToModel(*iter->second))) {
456 const TimeDelta pulse_delay(DeviceInfoUtil::CalculatePulseDelay(
457 GetLastUpdateTime(*iter->second), Time::Now()));
458 if (!pulse_delay.is_zero()) {
459 pulse_timer_.Start(FROM_HERE, pulse_delay,
460 base::Bind(&DeviceInfoService::SendLocalData,
461 base::Unretained(this)));
462 return;
463 }
464 }
465 SendLocalData();
466 }
467
468 void DeviceInfoService::SendLocalData() {
469 DCHECK(has_provider_initialized_);
470 // TODO(skym): Handle disconnecting and reconnecting, this will currently halt
471 // the pulse timer and never restart it.
472 if (!change_processor()) {
473 return;
474 }
475
476 std::unique_ptr<DeviceInfoSpecifics> specifics =
477 CopyToSpecifics(*local_device_info_provider_->GetLocalDeviceInfo());
478 specifics->set_last_updated_timestamp(syncer::TimeToProtoTime(Time::Now()));
479
480 std::unique_ptr<MetadataChangeList> metadata_change_list =
481 CreateMetadataChangeList();
482 change_processor()->Put(specifics->cache_guid(), CopyToEntityData(*specifics),
483 metadata_change_list.get());
484
485 std::unique_ptr<WriteBatch> batch = store_->CreateWriteBatch();
486 StoreSpecifics(std::move(specifics), batch.get());
487
488 CommitAndNotify(std::move(batch), std::move(metadata_change_list), true);
489 pulse_timer_.Start(
490 FROM_HERE, DeviceInfoUtil::kPulseInterval,
491 base::Bind(&DeviceInfoService::SendLocalData, base::Unretained(this)));
492 }
493
494 void DeviceInfoService::CommitAndNotify(
495 std::unique_ptr<WriteBatch> batch,
496 std::unique_ptr<MetadataChangeList> metadata_change_list,
497 bool should_notify) {
498 static_cast<SimpleMetadataChangeList*>(metadata_change_list.get())
499 ->TransferChanges(store_.get(), batch.get());
500 store_->CommitWriteBatch(
501 std::move(batch),
502 base::Bind(&DeviceInfoService::OnCommit, weak_factory_.GetWeakPtr()));
503 if (should_notify) {
504 NotifyObservers();
505 }
506 }
507
508 int DeviceInfoService::CountActiveDevices(const Time now) const {
509 return std::count_if(all_data_.begin(), all_data_.end(),
510 [now](ClientIdToSpecifics::const_reference pair) {
511 return DeviceInfoUtil::IsActive(
512 GetLastUpdateTime(*pair.second), now);
513 });
514 }
515
516 void DeviceInfoService::ReportStartupErrorToSync(const std::string& msg) {
517 DCHECK(!has_metadata_loaded_);
518 LOG(WARNING) << msg;
519
520 // Create a processor and give it the error in case sync tries to start.
521 if (!change_processor()) {
522 CreateChangeProcessor();
523 }
524 change_processor()->OnMetadataLoaded(
525 change_processor()->CreateAndUploadError(FROM_HERE, msg), nullptr);
526 }
527
528 // static
529 Time DeviceInfoService::GetLastUpdateTime(
530 const DeviceInfoSpecifics& specifics) {
531 if (specifics.has_last_updated_timestamp()) {
532 return syncer::ProtoTimeToTime(specifics.last_updated_timestamp());
533 } else {
534 return Time();
535 }
536 }
537
538 } // namespace sync_driver_v2
OLDNEW
« no previous file with comments | « components/sync_driver/device_info_service.h ('k') | components/sync_driver/device_info_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698