1 // Copyright (c) 2012 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/engine_impl/syncer.h"
6 
7 #include <memory>
8 #include <string>
9 
10 #include "base/auto_reset.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram_functions.h"
13 #include "base/trace_event/trace_event.h"
14 #include "components/sync/engine/sync_engine_switches.h"
15 #include "components/sync/engine_impl/cancelation_signal.h"
16 #include "components/sync/engine_impl/commit.h"
17 #include "components/sync/engine_impl/commit_processor.h"
18 #include "components/sync/engine_impl/cycle/nudge_tracker.h"
19 #include "components/sync/engine_impl/cycle/sync_cycle.h"
20 #include "components/sync/engine_impl/get_updates_delegate.h"
21 #include "components/sync/engine_impl/get_updates_processor.h"
22 #include "components/sync/engine_impl/net/server_connection_manager.h"
23 
24 namespace syncer {
25 
26 namespace {
27 
HandleCycleBegin(SyncCycle * cycle)28 void HandleCycleBegin(SyncCycle* cycle) {
29   cycle->mutable_status_controller()->UpdateStartTime();
30   cycle->SendEventNotification(SyncCycleEvent::SYNC_CYCLE_BEGIN);
31 }
32 
33 }  // namespace
34 
Syncer(CancelationSignal * cancelation_signal)35 Syncer::Syncer(CancelationSignal* cancelation_signal)
36     : cancelation_signal_(cancelation_signal), is_syncing_(false) {}
37 
~Syncer()38 Syncer::~Syncer() {}
39 
IsSyncing() const40 bool Syncer::IsSyncing() const {
41   return is_syncing_;
42 }
43 
NormalSyncShare(ModelTypeSet request_types,NudgeTracker * nudge_tracker,SyncCycle * cycle)44 bool Syncer::NormalSyncShare(ModelTypeSet request_types,
45                              NudgeTracker* nudge_tracker,
46                              SyncCycle* cycle) {
47   base::AutoReset<bool> is_syncing(&is_syncing_, true);
48   HandleCycleBegin(cycle);
49   if (nudge_tracker->IsGetUpdatesRequired(request_types)) {
50     VLOG(1) << "Downloading types " << ModelTypeSetToString(request_types);
51     if (!DownloadAndApplyUpdates(&request_types, cycle,
52                                  NormalGetUpdatesDelegate(*nudge_tracker))) {
53       return HandleCycleEnd(cycle, nudge_tracker->GetOrigin());
54     }
55   }
56 
57   SyncerError commit_result =
58       BuildAndPostCommits(request_types, nudge_tracker, cycle);
59   cycle->mutable_status_controller()->set_commit_result(commit_result);
60 
61   return HandleCycleEnd(cycle, nudge_tracker->GetOrigin());
62 }
63 
ConfigureSyncShare(const ModelTypeSet & request_types,sync_pb::SyncEnums::GetUpdatesOrigin origin,SyncCycle * cycle)64 bool Syncer::ConfigureSyncShare(const ModelTypeSet& request_types,
65                                 sync_pb::SyncEnums::GetUpdatesOrigin origin,
66                                 SyncCycle* cycle) {
67   base::AutoReset<bool> is_syncing(&is_syncing_, true);
68 
69   // It is possible during configuration that datatypes get unregistered from
70   // ModelTypeRegistry before scheduled configure sync cycle gets executed.
71   // This happens either because DataTypeController::LoadModels fail and type
72   // need to be stopped or during shutdown when all datatypes are stopped. When
73   // it happens we should adjust set of types to download to only include
74   // registered types.
75   ModelTypeSet still_enabled_types =
76       Intersection(request_types, cycle->context()->GetEnabledTypes());
77   VLOG(1) << "Configuring types " << ModelTypeSetToString(still_enabled_types);
78   HandleCycleBegin(cycle);
79   DownloadAndApplyUpdates(&still_enabled_types, cycle,
80                           ConfigureGetUpdatesDelegate(origin));
81   return HandleCycleEnd(cycle, origin);
82 }
83 
PollSyncShare(ModelTypeSet request_types,SyncCycle * cycle)84 bool Syncer::PollSyncShare(ModelTypeSet request_types, SyncCycle* cycle) {
85   base::AutoReset<bool> is_syncing(&is_syncing_, true);
86   VLOG(1) << "Polling types " << ModelTypeSetToString(request_types);
87   HandleCycleBegin(cycle);
88   DownloadAndApplyUpdates(&request_types, cycle, PollGetUpdatesDelegate());
89   return HandleCycleEnd(cycle, sync_pb::SyncEnums::PERIODIC);
90 }
91 
DownloadAndApplyUpdates(ModelTypeSet * request_types,SyncCycle * cycle,const GetUpdatesDelegate & delegate)92 bool Syncer::DownloadAndApplyUpdates(ModelTypeSet* request_types,
93                                      SyncCycle* cycle,
94                                      const GetUpdatesDelegate& delegate) {
95   // CommitOnlyTypes() should not be included in the GetUpdates, but should be
96   // included in the Commit. We are given a set of types for our SyncShare,
97   // and we must do this filtering. Note that |request_types| is also an out
98   // param, see below where we update it.
99   ModelTypeSet requested_commit_only_types =
100       Intersection(*request_types, CommitOnlyTypes());
101   ModelTypeSet download_types =
102       Difference(*request_types, requested_commit_only_types);
103   GetUpdatesProcessor get_updates_processor(
104       cycle->context()->model_type_registry()->update_handler_map(), delegate);
105   SyncerError download_result;
106   do {
107     download_result =
108         get_updates_processor.DownloadUpdates(&download_types, cycle);
109   } while (download_result.value() == SyncerError::SERVER_MORE_TO_DOWNLOAD);
110 
111   // It is our responsibility to propagate the removal of types that occurred in
112   // GetUpdatesProcessor::DownloadUpdates().
113   *request_types = Union(download_types, requested_commit_only_types);
114 
115   // Exit without applying if we're shutting down or an error was detected.
116   if (download_result.value() != SyncerError::SYNCER_OK || ExitRequested())
117     return false;
118 
119   {
120     TRACE_EVENT0("sync", "ApplyUpdates");
121 
122     // Apply updates to the other types. May or may not involve cross-thread
123     // traffic, depending on the underlying update handlers and the GU type's
124     // delegate.
125     get_updates_processor.ApplyUpdates(download_types,
126                                        cycle->mutable_status_controller());
127 
128     cycle->context()->set_hierarchy_conflict_detected(
129         cycle->status_controller().num_hierarchy_conflicts() > 0);
130     cycle->SendEventNotification(SyncCycleEvent::STATUS_CHANGED);
131   }
132 
133   return !ExitRequested();
134 }
135 
BuildAndPostCommits(const ModelTypeSet & request_types,NudgeTracker * nudge_tracker,SyncCycle * cycle)136 SyncerError Syncer::BuildAndPostCommits(const ModelTypeSet& request_types,
137                                         NudgeTracker* nudge_tracker,
138                                         SyncCycle* cycle) {
139   VLOG(1) << "Committing from types " << ModelTypeSetToString(request_types);
140 
141   CommitProcessor commit_processor(
142       request_types,
143       cycle->context()->model_type_registry()->commit_contributor_map());
144   // The ExitRequested() check is unnecessary, since we should start getting
145   // errors from the ServerConnectionManager if an exist has been requested.
146   // However, it doesn't hurt to check it anyway.
147   while (!ExitRequested()) {
148     std::unique_ptr<Commit> commit(Commit::Init(
149         cycle->context()->GetEnabledTypes(),
150         cycle->context()->max_commit_batch_size(),
151         cycle->context()->account_name(), cycle->context()->cache_guid(),
152         cycle->context()->cookie_jar_mismatch(),
153         cycle->context()->cookie_jar_empty(), cycle->context()->single_client(),
154         &commit_processor, cycle->context()->extensions_activity()));
155     if (!commit) {
156       break;
157     }
158 
159     SyncerError error = commit->PostAndProcessResponse(
160         nudge_tracker, cycle, cycle->mutable_status_controller(),
161         cycle->context()->extensions_activity());
162     base::UmaHistogramEnumeration("Sync.CommitResponse", error.value());
163     for (ModelType type : commit->GetContributingDataTypes()) {
164       const std::string kPrefix = "Sync.CommitResponse.";
165       base::UmaHistogramEnumeration(kPrefix + ModelTypeToHistogramSuffix(type),
166                                     error.value());
167     }
168     if (error.value() != SyncerError::SYNCER_OK) {
169       return error;
170     }
171   }
172 
173   return SyncerError(SyncerError::SYNCER_OK);
174 }
175 
ExitRequested()176 bool Syncer::ExitRequested() {
177   return cancelation_signal_->IsSignalled();
178 }
179 
HandleCycleEnd(SyncCycle * cycle,sync_pb::SyncEnums::GetUpdatesOrigin origin)180 bool Syncer::HandleCycleEnd(SyncCycle* cycle,
181                             sync_pb::SyncEnums::GetUpdatesOrigin origin) {
182   if (ExitRequested())
183     return false;
184 
185   bool success =
186       !HasSyncerError(cycle->status_controller().model_neutral_state());
187   if (success && origin == sync_pb::SyncEnums::PERIODIC) {
188     cycle->mutable_status_controller()->UpdatePollTime();
189   }
190   cycle->SendSyncCycleEndEventNotification(origin);
191 
192   return success;
193 }
194 
195 }  // namespace syncer
196