| Index: chrome/browser/sync/engine/all_status.cc
|
| ===================================================================
|
| --- chrome/browser/sync/engine/all_status.cc (revision 0)
|
| +++ chrome/browser/sync/engine/all_status.cc (revision 0)
|
| @@ -0,0 +1,335 @@
|
| +// Copyright (c) 2006-2009 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 "chrome/browser/sync/engine/all_status.h"
|
| +
|
| +#include <algorithm>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/port.h"
|
| +#include "base/rand_util.h"
|
| +#include "chrome/browser/sync/engine/auth_watcher.h"
|
| +#include "chrome/browser/sync/engine/net/gaia_authenticator.h"
|
| +#include "chrome/browser/sync/engine/net/server_connection_manager.h"
|
| +#include "chrome/browser/sync/engine/syncer.h"
|
| +#include "chrome/browser/sync/engine/syncer_thread.h"
|
| +#include "chrome/browser/sync/engine/syncproto.h"
|
| +#include "chrome/browser/sync/notifier/listener/talk_mediator.h"
|
| +#include "chrome/browser/sync/protocol/service_constants.h"
|
| +#include "chrome/browser/sync/syncable/directory_manager.h"
|
| +#include "chrome/browser/sync/util/event_sys-inl.h"
|
| +
|
| +namespace browser_sync {
|
| +
|
| +static const time_t kMinSyncObserveInterval = 10; // seconds
|
| +
|
| +// Backoff interval randomization factor.
|
| +static const int kBackoffRandomizationFactor = 2;
|
| +
|
| +const char* AllStatus::GetSyncStatusString(SyncStatus icon) {
|
| + const char* strings[] = {"OFFLINE", "OFFLINE_UNSYNCED", "SYNCING", "READY",
|
| + "CONFLICT", "OFFLINE_UNUSABLE"};
|
| + COMPILE_ASSERT(ARRAYSIZE(strings) == ICON_STATUS_COUNT, enum_indexed_array);
|
| + if (icon < 0 || icon >= ARRAYSIZE(strings))
|
| + LOG(FATAL) << "Illegal Icon State:" << icon;
|
| + return strings[icon];
|
| +}
|
| +
|
| +static const AllStatus::Status init_status =
|
| + { AllStatus::OFFLINE };
|
| +
|
| +static const AllStatusEvent shutdown_event =
|
| + { AllStatusEvent::SHUTDOWN, init_status };
|
| +
|
| +AllStatus::AllStatus() : channel_(new Channel(shutdown_event)),
|
| + status_(init_status) {
|
| + status_.initial_sync_ended = true;
|
| + status_.notifications_enabled = false;
|
| +}
|
| +
|
| +AllStatus::~AllStatus() {
|
| + delete channel_;
|
| +}
|
| +
|
| +void AllStatus::WatchConnectionManager(ServerConnectionManager* conn_mgr) {
|
| + conn_mgr_hookup_.reset(NewEventListenerHookup(conn_mgr->channel(), this,
|
| + &AllStatus::HandleServerConnectionEvent));
|
| +}
|
| +
|
| +void AllStatus::WatchAuthenticator(GaiaAuthenticator* gaia) {
|
| + gaia_hookup_.reset(NewEventListenerHookup(gaia->channel(), this,
|
| + &AllStatus::HandleGaiaAuthEvent));
|
| +}
|
| +
|
| +void AllStatus::WatchAuthWatcher(AuthWatcher* auth_watcher) {
|
| + authwatcher_hookup_.reset(
|
| + NewEventListenerHookup(auth_watcher->channel(), this,
|
| + &AllStatus::HandleAuthWatcherEvent));
|
| +}
|
| +
|
| +void AllStatus::WatchSyncerThread(SyncerThread* syncer_thread) {
|
| + syncer_thread_hookup_.reset(
|
| + NewEventListenerHookup(syncer_thread->channel(), this,
|
| + &AllStatus::HandleSyncerEvent));
|
| +}
|
| +
|
| +AllStatus::Status AllStatus::CreateBlankStatus() const {
|
| + Status status = status_;
|
| + status.syncing = true;
|
| + status.unsynced_count = 0;
|
| + status.conflicting_count = 0;
|
| + status.initial_sync_ended = false;
|
| + status.syncer_stuck = false;
|
| + status.max_consecutive_errors = 0;
|
| + status.server_broken = false;
|
| + status.updates_available = 0;
|
| + status.updates_received = 0;
|
| + return status;
|
| +}
|
| +
|
| +AllStatus::Status AllStatus::CalcSyncing(const SyncerEvent &event) const {
|
| + Status status = CreateBlankStatus();
|
| + SyncerStatus syncerStatus(event.last_session);
|
| + status.unsynced_count += syncerStatus.unsynced_count();
|
| + status.conflicting_count += syncerStatus.conflicting_commits();
|
| + if (syncerStatus.current_sync_timestamp() ==
|
| + syncerStatus.servers_latest_timestamp()) {
|
| + status.conflicting_count += syncerStatus.conflicting_updates();
|
| + }
|
| + status.syncing |= syncerStatus.syncing();
|
| + // Show a syncer as syncing if it's got stalled updates.
|
| + status.syncing = event.last_session->ShouldSyncAgain();
|
| + status.initial_sync_ended |= syncerStatus.IsShareUsable();
|
| + status.syncer_stuck |= syncerStatus.syncer_stuck();
|
| + if (syncerStatus.consecutive_errors() > status.max_consecutive_errors)
|
| + status.max_consecutive_errors = syncerStatus.consecutive_errors();
|
| +
|
| + // 100 is an arbitrary limit.
|
| + if (syncerStatus.consecutive_transient_error_commits() > 100)
|
| + status.server_broken = true;
|
| +
|
| + status.updates_available += syncerStatus.servers_latest_timestamp();
|
| + status.updates_received += syncerStatus.current_sync_timestamp();
|
| + return status;
|
| +}
|
| +
|
| +AllStatus::Status AllStatus::CalcSyncing() const {
|
| + return CreateBlankStatus();
|
| +}
|
| +
|
| +int AllStatus::CalcStatusChanges(Status* old_status) {
|
| + int what_changed = 0;
|
| +
|
| + // Calculate what changed and what the new icon should be.
|
| + if (status_.syncing != old_status->syncing)
|
| + what_changed |= AllStatusEvent::SYNCING;
|
| + if (status_.unsynced_count != old_status->unsynced_count)
|
| + what_changed |= AllStatusEvent::UNSYNCED_COUNT;
|
| + if (status_.server_up != old_status->server_up)
|
| + what_changed |= AllStatusEvent::SERVER_UP;
|
| + if (status_.server_reachable != old_status->server_reachable)
|
| + what_changed |= AllStatusEvent::SERVER_REACHABLE;
|
| + if (status_.notifications_enabled != old_status->notifications_enabled)
|
| + what_changed |= AllStatusEvent::NOTIFICATIONS_ENABLED;
|
| + if (status_.notifications_received != old_status->notifications_received)
|
| + what_changed |= AllStatusEvent::NOTIFICATIONS_RECEIVED;
|
| + if (status_.notifications_sent != old_status->notifications_sent)
|
| + what_changed |= AllStatusEvent::NOTIFICATIONS_SENT;
|
| + if (status_.initial_sync_ended != old_status->initial_sync_ended)
|
| + what_changed |= AllStatusEvent::INITIAL_SYNC_ENDED;
|
| + if (status_.authenticated != old_status->authenticated)
|
| + what_changed |= AllStatusEvent::AUTHENTICATED;
|
| +
|
| + const bool unsynced_changes = status_.unsynced_count > 0;
|
| + const bool online = status_.authenticated &&
|
| + status_.server_reachable && status_.server_up && !status_.server_broken;
|
| + if (online) {
|
| + if (status_.syncer_stuck)
|
| + status_.icon = CONFLICT;
|
| + else if (unsynced_changes || status_.syncing)
|
| + status_.icon = SYNCING;
|
| + else
|
| + status_.icon = READY;
|
| + } else if (!status_.initial_sync_ended) {
|
| + status_.icon = OFFLINE_UNUSABLE;
|
| + } else if (unsynced_changes) {
|
| + status_.icon = OFFLINE_UNSYNCED;
|
| + } else {
|
| + status_.icon = OFFLINE;
|
| + }
|
| +
|
| + if (status_.icon != old_status->icon)
|
| + what_changed |= AllStatusEvent::ICON;
|
| +
|
| + if (0 == what_changed)
|
| + return 0;
|
| + *old_status = status_;
|
| + return what_changed;
|
| +}
|
| +
|
| +void AllStatus::HandleGaiaAuthEvent(const GaiaAuthEvent& gaia_event) {
|
| + ScopedStatusLockWithNotify lock(this);
|
| + switch (gaia_event.what_happened) {
|
| + case GaiaAuthEvent::GAIA_AUTH_FAILED:
|
| + status_.authenticated = false;
|
| + break;
|
| + case GaiaAuthEvent::GAIA_AUTH_SUCCEEDED:
|
| + status_.authenticated = true;
|
| + break;
|
| + default:
|
| + lock.set_notify_plan(DONT_NOTIFY);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void AllStatus::HandleAuthWatcherEvent(const AuthWatcherEvent& auth_event) {
|
| + ScopedStatusLockWithNotify lock(this);
|
| + switch (auth_event.what_happened) {
|
| + case AuthWatcherEvent::GAIA_AUTH_FAILED:
|
| + case AuthWatcherEvent::SERVICE_AUTH_FAILED:
|
| + case AuthWatcherEvent::SERVICE_CONNECTION_FAILED:
|
| + case AuthWatcherEvent::AUTHENTICATION_ATTEMPT_START:
|
| + status_.authenticated = false;
|
| + break;
|
| + case AuthWatcherEvent::AUTH_SUCCEEDED:
|
| + // If we've already calculated that the server is reachable, since we've
|
| + // successfully authenticated, we can be confident that the server is up.
|
| + if (status_.server_reachable)
|
| + status_.server_up = true;
|
| +
|
| + if (!status_.authenticated) {
|
| + status_.authenticated = true;
|
| + status_ = CalcSyncing();
|
| + } else {
|
| + lock.set_notify_plan(DONT_NOTIFY);
|
| + }
|
| + break;
|
| + default:
|
| + lock.set_notify_plan(DONT_NOTIFY);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void AllStatus::HandleSyncerEvent(const SyncerEvent& event) {
|
| + ScopedStatusLockWithNotify lock(this);
|
| + switch (event.what_happened) {
|
| + case SyncerEvent::SYNC_CYCLE_ENDED:
|
| + case SyncerEvent::COMMITS_SUCCEEDED:
|
| + break;
|
| + case SyncerEvent::STATUS_CHANGED:
|
| + status_ = CalcSyncing(event);
|
| + break;
|
| + case SyncerEvent::SHUTDOWN_USE_WITH_CARE:
|
| + // We're safe to use this value here because we don't call into the syncer
|
| + // or block on any processes.
|
| + lock.set_notify_plan(DONT_NOTIFY);
|
| + break;
|
| + case SyncerEvent::OVER_QUOTA:
|
| + LOG(WARNING) << "User has gone over quota.";
|
| + lock.NotifyOverQuota();
|
| + break;
|
| + case SyncerEvent::REQUEST_SYNC_NUDGE:
|
| + lock.set_notify_plan(DONT_NOTIFY);
|
| + break;
|
| + default:
|
| + LOG(ERROR) << "Unrecognized Syncer Event: " << event.what_happened;
|
| + lock.set_notify_plan(DONT_NOTIFY);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +void AllStatus::HandleServerConnectionEvent(
|
| + const ServerConnectionEvent& event) {
|
| + if (ServerConnectionEvent::STATUS_CHANGED == event.what_happened) {
|
| + ScopedStatusLockWithNotify lock(this);
|
| + status_.server_up = IsGoodReplyFromServer(event.connection_code);
|
| + status_.server_reachable = event.server_reachable;
|
| + }
|
| +}
|
| +
|
| +void AllStatus::WatchTalkMediator(const TalkMediator* mediator) {
|
| + status_.notifications_enabled = false;
|
| + talk_mediator_hookup_.reset(
|
| + NewEventListenerHookup(mediator->channel(), this,
|
| + &AllStatus::HandleTalkMediatorEvent));
|
| +}
|
| +
|
| +void AllStatus::HandleTalkMediatorEvent(
|
| + const TalkMediatorEvent& event) {
|
| + ScopedStatusLockWithNotify lock(this);
|
| + switch (event.what_happened) {
|
| + case TalkMediatorEvent::SUBSCRIPTIONS_ON:
|
| + status_.notifications_enabled = true;
|
| + break;
|
| + case TalkMediatorEvent::LOGOUT_SUCCEEDED:
|
| + case TalkMediatorEvent::SUBSCRIPTIONS_OFF:
|
| + case TalkMediatorEvent::TALKMEDIATOR_DESTROYED:
|
| + status_.notifications_enabled = false;
|
| + break;
|
| + case TalkMediatorEvent::NOTIFICATION_RECEIVED:
|
| + status_.notifications_received++;
|
| + break;
|
| + case TalkMediatorEvent::NOTIFICATION_SENT:
|
| + status_.notifications_sent++;
|
| + break;
|
| + case TalkMediatorEvent::LOGIN_SUCCEEDED:
|
| + default:
|
| + lock.set_notify_plan(DONT_NOTIFY);
|
| + break;
|
| + }
|
| +}
|
| +
|
| +AllStatus::Status AllStatus::status() const {
|
| + MutexLock lock(&mutex_);
|
| + return status_;
|
| +}
|
| +
|
| +int AllStatus::GetRecommendedDelaySeconds(int base_delay_seconds) {
|
| + if (base_delay_seconds >= kMaxBackoffSeconds)
|
| + return kMaxBackoffSeconds;
|
| +
|
| + // This calculates approx. base_delay_seconds * 2 +/- base_delay_seconds / 2
|
| + int backoff_s = (0 == base_delay_seconds) ? 1 :
|
| + base_delay_seconds * kBackoffRandomizationFactor;
|
| +
|
| + // Flip a coin to randomize backoff interval by +/- 50%.
|
| + int rand_sign = base::RandInt(0, 1) * 2 - 1;
|
| +
|
| + // Truncation is adequate for rounding here.
|
| + backoff_s = backoff_s +
|
| + (rand_sign * (base_delay_seconds / kBackoffRandomizationFactor));
|
| +
|
| + // Cap the backoff interval.
|
| + backoff_s = std::min(backoff_s, kMaxBackoffSeconds);
|
| +
|
| + return backoff_s;
|
| +}
|
| +
|
| +int AllStatus::GetRecommendedDelay(int base_delay_ms) const {
|
| + return GetRecommendedDelaySeconds(base_delay_ms / 1000) * 1000;
|
| +}
|
| +
|
| +ScopedStatusLockWithNotify::ScopedStatusLockWithNotify(AllStatus* allstatus)
|
| + : allstatus_(allstatus), plan_(NOTIFY_IF_STATUS_CHANGED) {
|
| + event_.what_changed = 0;
|
| + allstatus->mutex_.Lock();
|
| + event_.status = allstatus->status_;
|
| +}
|
| +
|
| +ScopedStatusLockWithNotify::~ScopedStatusLockWithNotify() {
|
| + if (DONT_NOTIFY == plan_) {
|
| + allstatus_->mutex_.Unlock();
|
| + return;
|
| + }
|
| + event_.what_changed |= allstatus_->CalcStatusChanges(&event_.status);
|
| + allstatus_->mutex_.Unlock();
|
| + if (event_.what_changed)
|
| + allstatus_->channel()->NotifyListeners(event_);
|
| +}
|
| +
|
| +void ScopedStatusLockWithNotify::NotifyOverQuota() {
|
| + event_.what_changed |= AllStatusEvent::OVER_QUOTA;
|
| +}
|
| +
|
| +} // namespace browser_sync
|
|
|
| Property changes on: chrome\browser\sync\engine\all_status.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|