Home | History | Annotate | Download | only in engine
      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