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 45 Syncer::Syncer(syncer::CancelationSignal* cancelation_signal) 46 : cancelation_signal_(cancelation_signal) { 47 } 48 49 Syncer::~Syncer() {} 50 51 bool Syncer::ExitRequested() { 52 return cancelation_signal_->IsSignalled(); 53 } 54 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 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 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 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 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 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 187 void Syncer::HandleCycleBegin(SyncSession* session) { 188 session->mutable_status_controller()->UpdateStartTime(); 189 session->SendEventNotification(SyncEngineEvent::SYNC_CYCLE_BEGIN); 190 } 191 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