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 29using base::Time; 30using base::TimeDelta; 31using sync_pb::ClientCommand; 32 33namespace syncer { 34 35// TODO(akalin): We may want to propagate this switch up 36// eventually. 37#if defined(OS_ANDROID) || defined(OS_IOS) 38static const bool kCreateMobileBookmarksFolder = true; 39#else 40static const bool kCreateMobileBookmarksFolder = false; 41#endif 42 43using sessions::StatusController; 44using sessions::SyncSession; 45using sessions::NudgeTracker; 46 47Syncer::Syncer(syncer::CancelationSignal* cancelation_signal) 48 : cancelation_signal_(cancelation_signal) { 49} 50 51Syncer::~Syncer() {} 52 53bool Syncer::ExitRequested() { 54 return cancelation_signal_->IsSignalled(); 55} 56 57bool 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 87bool 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 105bool 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 121bool 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 162SyncerError 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 195void Syncer::HandleCycleBegin(SyncSession* session) { 196 session->mutable_status_controller()->UpdateStartTime(); 197 session->SendEventNotification(SyncCycleEvent::SYNC_CYCLE_BEGIN); 198} 199 200bool 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