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 47 Syncer::Syncer(syncer::CancelationSignal* cancelation_signal) 48 : cancelation_signal_(cancelation_signal) { 49 } 50 51 Syncer::~Syncer() {} 52 53 bool Syncer::ExitRequested() { 54 return cancelation_signal_->IsSignalled(); 55 } 56 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 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 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 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 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 195 void Syncer::HandleCycleBegin(SyncSession* session) { 196 session->mutable_status_controller()->UpdateStartTime(); 197 session->SendEventNotification(SyncCycleEvent::SYNC_CYCLE_BEGIN); 198 } 199 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