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 "sync/engine/syncer.h"
6
7 #include "base/debug/trace_event.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/time/time.h"
12 #include "build/build_config.h"
13 #include "sync/engine/apply_control_data_updates.h"
14 #include "sync/engine/commit.h"
15 #include "sync/engine/commit_processor.h"
16 #include "sync/engine/conflict_resolver.h"
17 #include "sync/engine/get_updates_delegate.h"
18 #include "sync/engine/get_updates_processor.h"
19 #include "sync/engine/net/server_connection_manager.h"
20 #include "sync/engine/syncer_types.h"
21 #include "sync/internal_api/public/base/cancelation_signal.h"
22 #include "sync/internal_api/public/base/unique_position.h"
23 #include "sync/internal_api/public/util/syncer_error.h"
24 #include "sync/sessions/nudge_tracker.h"
25 #include "sync/syncable/directory.h"
26 #include "sync/syncable/mutable_entry.h"
27 #include "sync/syncable/syncable-inl.h"
28
29 using base::Time;
30 using base::TimeDelta;
31 using sync_pb::ClientCommand;
32
33 namespace syncer {
34
35 // TODO(akalin): We may want to propagate this switch up
36 // eventually.
37 #if defined(OS_ANDROID) || defined(OS_IOS)
38 static const bool kCreateMobileBookmarksFolder = true;
39 #else
40 static const bool kCreateMobileBookmarksFolder = false;
41 #endif
42
43 using sessions::StatusController;
44 using sessions::SyncSession;
45 using sessions::NudgeTracker;
46
Syncer(syncer::CancelationSignal * cancelation_signal)47 Syncer::Syncer(syncer::CancelationSignal* cancelation_signal)
48 : cancelation_signal_(cancelation_signal) {
49 }
50
~Syncer()51 Syncer::~Syncer() {}
52
ExitRequested()53 bool Syncer::ExitRequested() {
54 return cancelation_signal_->IsSignalled();
55 }
56
NormalSyncShare(ModelTypeSet request_types,const NudgeTracker & nudge_tracker,SyncSession * session)57 bool Syncer::NormalSyncShare(ModelTypeSet request_types,
58 const NudgeTracker& nudge_tracker,
59 SyncSession* session) {
60 HandleCycleBegin(session);
61 if (nudge_tracker.IsGetUpdatesRequired() ||
62 session->context()->ShouldFetchUpdatesBeforeCommit()) {
63 VLOG(1) << "Downloading types " << ModelTypeSetToString(request_types);
64 NormalGetUpdatesDelegate normal_delegate(nudge_tracker);
65 GetUpdatesProcessor get_updates_processor(
66 session->context()->model_type_registry()->update_handler_map(),
67 normal_delegate);
68 if (!DownloadAndApplyUpdates(
69 request_types,
70 session,
71 &get_updates_processor,
72 kCreateMobileBookmarksFolder)) {
73 return HandleCycleEnd(session, nudge_tracker.GetLegacySource());
74 }
75 }
76
77 VLOG(1) << "Committing from types " << ModelTypeSetToString(request_types);
78 CommitProcessor commit_processor(
79 session->context()->model_type_registry()->commit_contributor_map());
80 SyncerError commit_result =
81 BuildAndPostCommits(request_types, session, &commit_processor);
82 session->mutable_status_controller()->set_commit_result(commit_result);
83
84 return HandleCycleEnd(session, nudge_tracker.GetLegacySource());
85 }
86
ConfigureSyncShare(ModelTypeSet request_types,sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,SyncSession * session)87 bool Syncer::ConfigureSyncShare(
88 ModelTypeSet request_types,
89 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,
90 SyncSession* session) {
91 VLOG(1) << "Configuring types " << ModelTypeSetToString(request_types);
92 HandleCycleBegin(session);
93 ConfigureGetUpdatesDelegate configure_delegate(source);
94 GetUpdatesProcessor get_updates_processor(
95 session->context()->model_type_registry()->update_handler_map(),
96 configure_delegate);
97 DownloadAndApplyUpdates(
98 request_types,
99 session,
100 &get_updates_processor,
101 kCreateMobileBookmarksFolder);
102 return HandleCycleEnd(session, source);
103 }
104
PollSyncShare(ModelTypeSet request_types,SyncSession * session)105 bool Syncer::PollSyncShare(ModelTypeSet request_types,
106 SyncSession* session) {
107 VLOG(1) << "Polling types " << ModelTypeSetToString(request_types);
108 HandleCycleBegin(session);
109 PollGetUpdatesDelegate poll_delegate;
110 GetUpdatesProcessor get_updates_processor(
111 session->context()->model_type_registry()->update_handler_map(),
112 poll_delegate);
113 DownloadAndApplyUpdates(
114 request_types,
115 session,
116 &get_updates_processor,
117 kCreateMobileBookmarksFolder);
118 return HandleCycleEnd(session, sync_pb::GetUpdatesCallerInfo::PERIODIC);
119 }
120
DownloadAndApplyUpdates(ModelTypeSet request_types,SyncSession * session,GetUpdatesProcessor * get_updates_processor,bool create_mobile_bookmarks_folder)121 bool Syncer::DownloadAndApplyUpdates(
122 ModelTypeSet request_types,
123 SyncSession* session,
124 GetUpdatesProcessor* get_updates_processor,
125 bool create_mobile_bookmarks_folder) {
126 SyncerError download_result = UNSET;
127 do {
128 download_result = get_updates_processor->DownloadUpdates(
129 request_types,
130 session,
131 create_mobile_bookmarks_folder);
132 } while (download_result == SERVER_MORE_TO_DOWNLOAD);
133
134 // Exit without applying if we're shutting down or an error was detected.
135 if (download_result != SYNCER_OK)
136 return false;
137 if (ExitRequested())
138 return false;
139
140 {
141 TRACE_EVENT0("sync", "ApplyUpdates");
142
143 // Control type updates always get applied first.
144 ApplyControlDataUpdates(session->context()->directory());
145
146 // Apply upates to the other types. May or may not involve cross-thread
147 // traffic, depending on the underlying update handlers and the GU type's
148 // delegate.
149 get_updates_processor->ApplyUpdates(request_types,
150 session->mutable_status_controller());
151
152 session->context()->set_hierarchy_conflict_detected(
153 session->status_controller().num_hierarchy_conflicts() > 0);
154 session->SendEventNotification(SyncCycleEvent::STATUS_CHANGED);
155 }
156
157 if (ExitRequested())
158 return false;
159 return true;
160 }
161
BuildAndPostCommits(ModelTypeSet requested_types,sessions::SyncSession * session,CommitProcessor * commit_processor)162 SyncerError Syncer::BuildAndPostCommits(ModelTypeSet requested_types,
163 sessions::SyncSession* session,
164 CommitProcessor* commit_processor) {
165 // The ExitRequested() check is unnecessary, since we should start getting
166 // errors from the ServerConnectionManager if an exist has been requested.
167 // However, it doesn't hurt to check it anyway.
168 while (!ExitRequested()) {
169 scoped_ptr<Commit> commit(
170 Commit::Init(
171 requested_types,
172 session->context()->GetEnabledTypes(),
173 session->context()->max_commit_batch_size(),
174 session->context()->account_name(),
175 session->context()->directory()->cache_guid(),
176 commit_processor,
177 session->context()->extensions_activity()));
178 if (!commit) {
179 break;
180 }
181
182 SyncerError error = commit->PostAndProcessResponse(
183 session,
184 session->mutable_status_controller(),
185 session->context()->extensions_activity());
186 commit->CleanUp();
187 if (error != SYNCER_OK) {
188 return error;
189 }
190 }
191
192 return SYNCER_OK;
193 }
194
HandleCycleBegin(SyncSession * session)195 void Syncer::HandleCycleBegin(SyncSession* session) {
196 session->mutable_status_controller()->UpdateStartTime();
197 session->SendEventNotification(SyncCycleEvent::SYNC_CYCLE_BEGIN);
198 }
199
HandleCycleEnd(SyncSession * session,sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source)200 bool Syncer::HandleCycleEnd(
201 SyncSession* session,
202 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source) {
203 if (!ExitRequested()) {
204 session->SendSyncCycleEndEventNotification(source);
205 return true;
206 } else {
207 return false;
208 }
209 }
210
211 } // namespace syncer
212