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.
5 #include "sync/engine/syncer.h"
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/download.h"
18 #include "sync/engine/get_updates_delegate.h"
19 #include "sync/engine/get_updates_processor.h"
20 #include "sync/engine/net/server_connection_manager.h"
21 #include "sync/engine/syncer_types.h"
22 #include "sync/internal_api/public/base/cancelation_signal.h"
23 #include "sync/internal_api/public/base/unique_position.h"
24 #include "sync/internal_api/public/util/syncer_error.h"
25 #include "sync/sessions/nudge_tracker.h"
26 #include "sync/syncable/directory.h"
27 #include "sync/syncable/mutable_entry.h"
28 #include "sync/syncable/syncable-inl.h"
31 using base::TimeDelta;
32 using sync_pb::ClientCommand;
36 // TODO(akalin): We may want to propagate this switch up
38 #if defined(OS_ANDROID) || defined(OS_IOS)
39 static const bool kCreateMobileBookmarksFolder = true;
41 static const bool kCreateMobileBookmarksFolder = false;
44 using sessions::StatusController;
45 using sessions::SyncSession;
46 using sessions::NudgeTracker;
48 Syncer::Syncer(syncer::CancelationSignal* cancelation_signal)
49 : cancelation_signal_(cancelation_signal) {
54 bool Syncer::ExitRequested() {
55 return cancelation_signal_->IsSignalled();
58 bool Syncer::NormalSyncShare(ModelTypeSet request_types,
59 const NudgeTracker& nudge_tracker,
60 SyncSession* session) {
61 HandleCycleBegin(session);
62 if (nudge_tracker.IsGetUpdatesRequired() ||
63 session->context()->ShouldFetchUpdatesBeforeCommit()) {
64 VLOG(1) << "Downloading types " << ModelTypeSetToString(request_types);
65 NormalGetUpdatesDelegate normal_delegate(nudge_tracker);
66 GetUpdatesProcessor get_updates_processor(
67 session->context()->model_type_registry()->update_handler_map(),
69 if (!DownloadAndApplyUpdates(
72 &get_updates_processor,
73 kCreateMobileBookmarksFolder)) {
74 return HandleCycleEnd(session, nudge_tracker.updates_source());
78 VLOG(1) << "Committing from types " << ModelTypeSetToString(request_types);
79 CommitProcessor commit_processor(
80 session->context()->model_type_registry()->commit_contributor_map());
81 SyncerError commit_result =
82 BuildAndPostCommits(request_types, session, &commit_processor);
83 session->mutable_status_controller()->set_commit_result(commit_result);
85 return HandleCycleEnd(session, nudge_tracker.updates_source());
88 bool Syncer::ConfigureSyncShare(
89 ModelTypeSet request_types,
90 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,
91 SyncSession* session) {
92 VLOG(1) << "Configuring types " << ModelTypeSetToString(request_types);
93 HandleCycleBegin(session);
94 ConfigureGetUpdatesDelegate configure_delegate(source);
95 GetUpdatesProcessor get_updates_processor(
96 session->context()->model_type_registry()->update_handler_map(),
98 DownloadAndApplyUpdates(
101 &get_updates_processor,
102 kCreateMobileBookmarksFolder);
103 return HandleCycleEnd(session, source);
106 bool Syncer::PollSyncShare(ModelTypeSet request_types,
107 SyncSession* session) {
108 VLOG(1) << "Polling types " << ModelTypeSetToString(request_types);
109 HandleCycleBegin(session);
110 PollGetUpdatesDelegate poll_delegate;
111 GetUpdatesProcessor get_updates_processor(
112 session->context()->model_type_registry()->update_handler_map(),
114 DownloadAndApplyUpdates(
117 &get_updates_processor,
118 kCreateMobileBookmarksFolder);
119 return HandleCycleEnd(session, sync_pb::GetUpdatesCallerInfo::PERIODIC);
122 bool Syncer::RetrySyncShare(ModelTypeSet request_types,
123 SyncSession* session) {
124 VLOG(1) << "Retrying types " << ModelTypeSetToString(request_types);
125 HandleCycleBegin(session);
126 RetryGetUpdatesDelegate retry_delegate;
127 GetUpdatesProcessor get_updates_processor(
128 session->context()->model_type_registry()->update_handler_map(),
130 DownloadAndApplyUpdates(
133 &get_updates_processor,
134 kCreateMobileBookmarksFolder);
135 return HandleCycleEnd(session, sync_pb::GetUpdatesCallerInfo::RETRY);
138 bool Syncer::DownloadAndApplyUpdates(
139 ModelTypeSet request_types,
140 SyncSession* session,
141 GetUpdatesProcessor* get_updates_processor,
142 bool create_mobile_bookmarks_folder) {
143 SyncerError download_result = UNSET;
145 TRACE_EVENT0("sync", "DownloadUpdates");
146 sync_pb::ClientToServerMessage msg;
147 sync_pb::GetUpdatesMessage* gu_msg = msg.mutable_get_updates();
149 download::InitDownloadUpdatesContext(
150 session, create_mobile_bookmarks_folder, &msg);
151 get_updates_processor->PrepareGetUpdates(request_types, gu_msg);
153 download_result = download::ExecuteDownloadUpdates(request_types,
155 get_updates_processor,
157 session->mutable_status_controller()->set_last_download_updates_result(
159 } while (download_result == SERVER_MORE_TO_DOWNLOAD);
161 // Exit without applying if we're shutting down or an error was detected.
162 if (download_result != SYNCER_OK)
168 TRACE_EVENT0("sync", "ApplyUpdates");
170 // Control type updates always get applied first.
171 ApplyControlDataUpdates(session->context()->directory());
173 // Apply upates to the other types. May or may not involve cross-thread
174 // traffic, depending on the underlying update handlers and the GU type's
176 get_updates_processor->ApplyUpdates(session->mutable_status_controller());
178 session->context()->set_hierarchy_conflict_detected(
179 session->status_controller().num_hierarchy_conflicts() > 0);
180 session->SendEventNotification(SyncCycleEvent::STATUS_CHANGED);
188 SyncerError Syncer::BuildAndPostCommits(ModelTypeSet requested_types,
189 sessions::SyncSession* session,
190 CommitProcessor* commit_processor) {
191 // The ExitRequested() check is unnecessary, since we should start getting
192 // errors from the ServerConnectionManager if an exist has been requested.
193 // However, it doesn't hurt to check it anyway.
194 while (!ExitRequested()) {
195 scoped_ptr<Commit> commit(
198 session->context()->enabled_types(),
199 session->context()->max_commit_batch_size(),
200 session->context()->account_name(),
201 session->context()->directory()->cache_guid(),
203 session->context()->extensions_activity()));
208 SyncerError error = commit->PostAndProcessResponse(
210 session->mutable_status_controller(),
211 session->context()->extensions_activity());
213 if (error != SYNCER_OK) {
221 void Syncer::HandleCycleBegin(SyncSession* session) {
222 session->mutable_status_controller()->UpdateStartTime();
223 session->SendEventNotification(SyncCycleEvent::SYNC_CYCLE_BEGIN);
226 bool Syncer::HandleCycleEnd(
227 SyncSession* session,
228 sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source) {
229 if (!ExitRequested()) {
230 session->SendSyncCycleEndEventNotification(source);
237 } // namespace syncer