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