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

Side by Side Diff: components/sync/device_info/device_info_service.cc

Issue 2460903003: [Sync] Rename DeviceInfoService to DeviceInfoSyncBridge. (Closed)
Patch Set: Updating nullptr to null in comment. Created 4 years, 1 month 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/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
OLDNEW
« no previous file with comments | « components/sync/device_info/device_info_service.h ('k') | components/sync/device_info/device_info_service_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698