1// Copyright (c) 2011 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 "chrome/browser/sync/engine/all_status.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "base/port.h"
11#include "chrome/browser/sync/engine/net/server_connection_manager.h"
12#include "chrome/browser/sync/protocol/service_constants.h"
13#include "chrome/browser/sync/sessions/session_state.h"
14#include "chrome/browser/sync/syncable/directory_manager.h"
15
16namespace browser_sync {
17
18static const sync_api::SyncManager::Status init_status =
19  { sync_api::SyncManager::Status::OFFLINE };
20
21AllStatus::AllStatus() : status_(init_status) {
22  status_.initial_sync_ended = true;
23  status_.notifications_enabled = false;
24}
25
26AllStatus::~AllStatus() {
27}
28
29sync_api::SyncManager::Status AllStatus::CreateBlankStatus() const {
30  // Status is initialized with the previous status value.  Variables
31  // whose values accumulate (e.g. lifetime counters like updates_received)
32  // are not to be cleared here.
33  sync_api::SyncManager::Status status = status_;
34  status.syncing = true;
35  status.unsynced_count = 0;
36  status.conflicting_count = 0;
37  status.initial_sync_ended = false;
38  status.syncer_stuck = false;
39  status.max_consecutive_errors = 0;
40  status.server_broken = false;
41  status.updates_available = 0;
42  return status;
43}
44
45sync_api::SyncManager::Status AllStatus::CalcSyncing(
46    const SyncEngineEvent &event) const {
47  sync_api::SyncManager::Status status = CreateBlankStatus();
48  const sessions::SyncSessionSnapshot* snapshot = event.snapshot;
49  status.unsynced_count += static_cast<int>(snapshot->unsynced_count);
50  status.conflicting_count += snapshot->errors.num_conflicting_commits;
51  // The syncer may not be done yet, which could cause conflicting updates.
52  // But this is only used for status, so it is better to have visibility.
53  status.conflicting_count += snapshot->num_conflicting_updates;
54
55  status.syncing |= snapshot->syncer_status.syncing;
56  status.syncing = snapshot->has_more_to_sync && snapshot->is_silenced;
57  status.initial_sync_ended |= snapshot->is_share_usable;
58  status.syncer_stuck |= snapshot->syncer_status.syncer_stuck;
59
60  const sessions::ErrorCounters& errors(snapshot->errors);
61  if (errors.consecutive_errors > status.max_consecutive_errors)
62    status.max_consecutive_errors = errors.consecutive_errors;
63
64  // 100 is an arbitrary limit.
65  if (errors.consecutive_transient_error_commits > 100)
66    status.server_broken = true;
67
68  status.updates_available += snapshot->num_server_changes_remaining;
69
70  // Accumulate update count only once per session to avoid double-counting.
71  // TODO(ncarter): Make this realtime by having the syncer_status
72  // counter preserve its value across sessions.  http://crbug.com/26339
73  if (event.what_happened == SyncEngineEvent::SYNC_CYCLE_ENDED) {
74    status.updates_received +=
75        snapshot->syncer_status.num_updates_downloaded_total;
76    status.tombstone_updates_received +=
77        snapshot->syncer_status.num_tombstone_updates_downloaded_total;
78  }
79  return status;
80}
81
82void AllStatus::CalcStatusChanges() {
83  const bool unsynced_changes = status_.unsynced_count > 0;
84  const bool online = status_.authenticated &&
85    status_.server_reachable && status_.server_up && !status_.server_broken;
86  if (online) {
87    if (status_.syncer_stuck)
88      status_.summary = sync_api::SyncManager::Status::CONFLICT;
89    else if (unsynced_changes || status_.syncing)
90      status_.summary = sync_api::SyncManager::Status::SYNCING;
91    else
92      status_.summary = sync_api::SyncManager::Status::READY;
93  } else if (!status_.initial_sync_ended) {
94    status_.summary = sync_api::SyncManager::Status::OFFLINE_UNUSABLE;
95  } else if (unsynced_changes) {
96    status_.summary = sync_api::SyncManager::Status::OFFLINE_UNSYNCED;
97  } else {
98    status_.summary = sync_api::SyncManager::Status::OFFLINE;
99  }
100}
101
102void AllStatus::OnSyncEngineEvent(const SyncEngineEvent& event) {
103  ScopedStatusLock lock(this);
104  switch (event.what_happened) {
105    case SyncEngineEvent::SYNC_CYCLE_ENDED:
106    case SyncEngineEvent::STATUS_CHANGED:
107      status_ = CalcSyncing(event);
108      break;
109    case SyncEngineEvent::STOP_SYNCING_PERMANENTLY:
110    case SyncEngineEvent::UPDATED_TOKEN:
111    case SyncEngineEvent::CLEAR_SERVER_DATA_FAILED:
112    case SyncEngineEvent::CLEAR_SERVER_DATA_SUCCEEDED:
113       break;
114    default:
115      LOG(ERROR) << "Unrecognized Syncer Event: " << event.what_happened;
116      break;
117  }
118}
119
120void AllStatus::HandleServerConnectionEvent(
121    const ServerConnectionEvent& event) {
122  if (ServerConnectionEvent::STATUS_CHANGED == event.what_happened) {
123    ScopedStatusLock lock(this);
124    status_.server_up = IsGoodReplyFromServer(event.connection_code);
125    status_.server_reachable = event.server_reachable;
126
127    if (event.connection_code == HttpResponse::SERVER_CONNECTION_OK) {
128      status_.authenticated = true;
129    } else {
130      status_.authenticated = false;
131    }
132  }
133}
134
135sync_api::SyncManager::Status AllStatus::status() const {
136  base::AutoLock lock(mutex_);
137  return status_;
138}
139
140void AllStatus::SetNotificationsEnabled(bool notifications_enabled) {
141  ScopedStatusLock lock(this);
142  status_.notifications_enabled = notifications_enabled;
143}
144
145void AllStatus::IncrementNotificationsSent() {
146  ScopedStatusLock lock(this);
147  ++status_.notifications_sent;
148}
149
150void AllStatus::IncrementNotificationsReceived() {
151  ScopedStatusLock lock(this);
152  ++status_.notifications_received;
153}
154
155ScopedStatusLock::ScopedStatusLock(AllStatus* allstatus)
156    : allstatus_(allstatus) {
157  allstatus->mutex_.Acquire();
158}
159
160ScopedStatusLock::~ScopedStatusLock() {
161  allstatus_->CalcStatusChanges();
162  allstatus_->mutex_.Release();
163}
164
165}  // namespace browser_sync
166