普通文本  |  204行  |  6.57 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "sync/engine/syncer.h"

#include "base/debug/trace_event.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/message_loop/message_loop.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "sync/engine/apply_control_data_updates.h"
#include "sync/engine/commit.h"
#include "sync/engine/conflict_resolver.h"
#include "sync/engine/download.h"
#include "sync/engine/net/server_connection_manager.h"
#include "sync/engine/syncer_types.h"
#include "sync/internal_api/public/base/cancelation_signal.h"
#include "sync/internal_api/public/base/unique_position.h"
#include "sync/internal_api/public/util/syncer_error.h"
#include "sync/sessions/nudge_tracker.h"
#include "sync/syncable/directory.h"
#include "sync/syncable/mutable_entry.h"
#include "sync/syncable/syncable-inl.h"

using base::Time;
using base::TimeDelta;
using sync_pb::ClientCommand;

namespace syncer {

// TODO(akalin): We may want to propagate this switch up
// eventually.
#if defined(OS_ANDROID) || defined(OS_IOS)
static const bool kCreateMobileBookmarksFolder = true;
#else
static const bool kCreateMobileBookmarksFolder = false;
#endif

using sessions::StatusController;
using sessions::SyncSession;
using sessions::NudgeTracker;

Syncer::Syncer(syncer::CancelationSignal* cancelation_signal)
    : cancelation_signal_(cancelation_signal) {
}

Syncer::~Syncer() {}

bool Syncer::ExitRequested() {
  return cancelation_signal_->IsSignalled();
}

bool Syncer::NormalSyncShare(ModelTypeSet request_types,
                             const NudgeTracker& nudge_tracker,
                             SyncSession* session) {
  HandleCycleBegin(session);
  VLOG(1) << "Downloading types " << ModelTypeSetToString(request_types);
  if (nudge_tracker.IsGetUpdatesRequired() ||
      session->context()->ShouldFetchUpdatesBeforeCommit()) {
    if (!DownloadAndApplyUpdates(
            request_types,
            session,
            base::Bind(&download::BuildNormalDownloadUpdates,
                       session,
                       kCreateMobileBookmarksFolder,
                       request_types,
                       base::ConstRef(nudge_tracker)))) {
      return HandleCycleEnd(session, nudge_tracker.updates_source());
    }
  }

  VLOG(1) << "Committing from types " << ModelTypeSetToString(request_types);
  SyncerError commit_result = BuildAndPostCommits(request_types, session);
  session->mutable_status_controller()->set_commit_result(commit_result);

  return HandleCycleEnd(session, nudge_tracker.updates_source());
}

bool Syncer::ConfigureSyncShare(
    ModelTypeSet request_types,
    sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source,
    SyncSession* session) {
  HandleCycleBegin(session);
  VLOG(1) << "Configuring types " << ModelTypeSetToString(request_types);
  DownloadAndApplyUpdates(
      request_types,
      session,
      base::Bind(&download::BuildDownloadUpdatesForConfigure,
                 session,
                 kCreateMobileBookmarksFolder,
                 source,
                 request_types));
  return HandleCycleEnd(session, source);
}

bool Syncer::PollSyncShare(ModelTypeSet request_types,
                           SyncSession* session) {
  HandleCycleBegin(session);
  VLOG(1) << "Polling types " << ModelTypeSetToString(request_types);
  DownloadAndApplyUpdates(
      request_types,
      session,
      base::Bind(&download::BuildDownloadUpdatesForPoll,
                 session,
                 kCreateMobileBookmarksFolder,
                 request_types));
  return HandleCycleEnd(session, sync_pb::GetUpdatesCallerInfo::PERIODIC);
}

void Syncer::ApplyUpdates(SyncSession* session) {
  TRACE_EVENT0("sync", "ApplyUpdates");

  ApplyControlDataUpdates(session->context()->directory());

  UpdateHandlerMap* handler_map = session->context()->update_handler_map();
  for (UpdateHandlerMap::iterator it = handler_map->begin();
       it != handler_map->end(); ++it) {
    it->second->ApplyUpdates(session->mutable_status_controller());
  }

  session->context()->set_hierarchy_conflict_detected(
      session->status_controller().num_hierarchy_conflicts() > 0);

  session->SendEventNotification(SyncEngineEvent::STATUS_CHANGED);
}

bool Syncer::DownloadAndApplyUpdates(
    ModelTypeSet request_types,
    SyncSession* session,
    base::Callback<void(sync_pb::ClientToServerMessage*)> build_fn) {
  SyncerError download_result = UNSET;
  do {
    TRACE_EVENT0("sync", "DownloadUpdates");
    sync_pb::ClientToServerMessage msg;
    build_fn.Run(&msg);
    download_result =
        download::ExecuteDownloadUpdates(request_types, session, &msg);
    session->mutable_status_controller()->set_last_download_updates_result(
        download_result);
  } while (download_result == SERVER_MORE_TO_DOWNLOAD);

  // Exit without applying if we're shutting down or an error was detected.
  if (download_result != SYNCER_OK)
    return false;
  if (ExitRequested())
    return false;

  ApplyUpdates(session);
  if (ExitRequested())
    return false;
  return true;
}

SyncerError Syncer::BuildAndPostCommits(ModelTypeSet requested_types,
                                        sessions::SyncSession* session) {
  // The ExitRequested() check is unnecessary, since we should start getting
  // errors from the ServerConnectionManager if an exist has been requested.
  // However, it doesn't hurt to check it anyway.
  while (!ExitRequested()) {
    scoped_ptr<Commit> commit(
        Commit::Init(
            requested_types,
            session->context()->max_commit_batch_size(),
            session->context()->account_name(),
            session->context()->directory()->cache_guid(),
            session->context()->commit_contributor_map(),
            session->context()->extensions_activity()));
    if (!commit) {
      break;
    }

    SyncerError error = commit->PostAndProcessResponse(
        session,
        session->mutable_status_controller(),
        session->context()->extensions_activity());
    commit->CleanUp();
    if (error != SYNCER_OK) {
      return error;
    }
  }

  return SYNCER_OK;
}

void Syncer::HandleCycleBegin(SyncSession* session) {
  session->mutable_status_controller()->UpdateStartTime();
  session->SendEventNotification(SyncEngineEvent::SYNC_CYCLE_BEGIN);
}

bool Syncer::HandleCycleEnd(
    SyncSession* session,
    sync_pb::GetUpdatesCallerInfo::GetUpdatesSource source) {
  if (!ExitRequested()) {
    session->SendSyncCycleEndEventNotification(source);
    return true;
  } else {
    return false;
  }
}

}  // namespace syncer