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 "chrome/browser/sync/backend_migrator.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/tracked_objects.h"
10#include "chrome/browser/chrome_notification_types.h"
11#include "chrome/browser/sync/profile_sync_service.h"
12#include "content/public/browser/notification_details.h"
13#include "content/public/browser/notification_source.h"
14#include "sync/internal_api/public/configure_reason.h"
15#include "sync/internal_api/public/read_transaction.h"
16#include "sync/protocol/sync.pb.h"
17#include "sync/syncable/directory.h" // TODO(tim): Bug 131130.
18
19using syncer::ModelTypeSet;
20
21namespace browser_sync {
22
23using syncer::ModelTypeToString;
24
25MigrationObserver::~MigrationObserver() {}
26
27BackendMigrator::BackendMigrator(const std::string& name,
28                                 syncer::UserShare* user_share,
29                                 ProfileSyncService* service,
30                                 sync_driver::DataTypeManager* manager,
31                                 const base::Closure &migration_done_callback)
32    : name_(name), user_share_(user_share), service_(service),
33      manager_(manager), state_(IDLE),
34      migration_done_callback_(migration_done_callback),
35      weak_ptr_factory_(this) {
36}
37
38BackendMigrator::~BackendMigrator() {
39}
40
41// Helper macros to log with the syncer thread name; useful when there
42// are multiple syncer threads involved.
43
44#define SLOG(severity) LOG(severity) << name_ << ": "
45
46#define SDVLOG(verbose_level) DVLOG(verbose_level) << name_ << ": "
47
48void BackendMigrator::MigrateTypes(syncer::ModelTypeSet types) {
49  const ModelTypeSet old_to_migrate = to_migrate_;
50  to_migrate_.PutAll(types);
51  SDVLOG(1) << "MigrateTypes called with " << ModelTypeSetToString(types)
52           << ", old_to_migrate = " << ModelTypeSetToString(old_to_migrate)
53          << ", to_migrate_ = " << ModelTypeSetToString(to_migrate_);
54  if (old_to_migrate.Equals(to_migrate_)) {
55    SDVLOG(1) << "MigrateTypes called with no new types; ignoring";
56    return;
57  }
58
59  if (state_ == IDLE)
60    ChangeState(WAITING_TO_START);
61
62  if (state_ == WAITING_TO_START) {
63    if (!TryStart())
64      SDVLOG(1) << "Manager not configured; waiting";
65    return;
66  }
67
68  DCHECK_GT(state_, WAITING_TO_START);
69  // If we're already migrating, interrupt the current migration.
70  RestartMigration();
71}
72
73void BackendMigrator::AddMigrationObserver(MigrationObserver* observer) {
74  migration_observers_.AddObserver(observer);
75}
76
77bool BackendMigrator::HasMigrationObserver(
78    MigrationObserver* observer) const {
79  return migration_observers_.HasObserver(observer);
80}
81
82void BackendMigrator::RemoveMigrationObserver(MigrationObserver* observer) {
83  migration_observers_.RemoveObserver(observer);
84}
85
86void BackendMigrator::ChangeState(State new_state) {
87  state_ = new_state;
88  FOR_EACH_OBSERVER(MigrationObserver, migration_observers_,
89                    OnMigrationStateChange());
90}
91
92bool BackendMigrator::TryStart() {
93  DCHECK_EQ(state_, WAITING_TO_START);
94  if (manager_->state() == sync_driver::DataTypeManager::CONFIGURED) {
95    RestartMigration();
96    return true;
97  }
98  return false;
99}
100
101void BackendMigrator::RestartMigration() {
102  // We'll now disable any running types that need to be migrated.
103  ChangeState(DISABLING_TYPES);
104  SDVLOG(1) << "BackendMigrator disabling types "
105            << ModelTypeSetToString(to_migrate_);
106
107  manager_->PurgeForMigration(to_migrate_, syncer::CONFIGURE_REASON_MIGRATION);
108}
109
110void BackendMigrator::OnConfigureDone(
111    const sync_driver::DataTypeManager::ConfigureResult& result) {
112  if (state_ == IDLE)
113    return;
114
115  // |manager_|'s methods aren't re-entrant, and we're notified from
116  // them, so post a task to avoid problems.
117  SDVLOG(1) << "Posting OnConfigureDoneImpl";
118  base::MessageLoop::current()->PostTask(
119      FROM_HERE,
120      base::Bind(&BackendMigrator::OnConfigureDoneImpl,
121                 weak_ptr_factory_.GetWeakPtr(), result));
122}
123
124namespace {
125
126syncer::ModelTypeSet GetUnsyncedDataTypes(syncer::UserShare* user_share) {
127  syncer::ReadTransaction trans(FROM_HERE, user_share);
128  syncer::ModelTypeSet unsynced_data_types;
129  for (int i = syncer::FIRST_REAL_MODEL_TYPE;
130       i < syncer::MODEL_TYPE_COUNT; ++i) {
131    syncer::ModelType type = syncer::ModelTypeFromInt(i);
132    sync_pb::DataTypeProgressMarker progress_marker;
133    trans.GetDirectory()->GetDownloadProgress(type, &progress_marker);
134    if (progress_marker.token().empty()) {
135      unsynced_data_types.Put(type);
136    }
137  }
138  return unsynced_data_types;
139}
140
141}  // namespace
142
143void BackendMigrator::OnConfigureDoneImpl(
144    const sync_driver::DataTypeManager::ConfigureResult& result) {
145  SDVLOG(1) << "OnConfigureDone with requested types "
146            << ModelTypeSetToString(result.requested_types)
147            << ", status " << result.status
148            << ", and to_migrate_ = " << ModelTypeSetToString(to_migrate_);
149  if (state_ == WAITING_TO_START) {
150    if (!TryStart())
151      SDVLOG(1) << "Manager still not configured; still waiting";
152    return;
153  }
154
155  DCHECK_GT(state_, WAITING_TO_START);
156
157  const ModelTypeSet intersection =
158      Intersection(result.requested_types, to_migrate_);
159  // This intersection check is to determine if our disable request
160  // was interrupted by a user changing preferred types.
161  if (state_ == DISABLING_TYPES && !intersection.Empty()) {
162    SDVLOG(1) << "Disable request interrupted by user changing types";
163    RestartMigration();
164    return;
165  }
166
167  if (result.status != sync_driver::DataTypeManager::OK) {
168    // If this fails, and we're disabling types, a type may or may not be
169    // disabled until the user restarts the browser.  If this wasn't an abort,
170    // any failure will be reported as an unrecoverable error to the UI. If it
171    // was an abort, then typically things are shutting down anyway. There isn't
172    // much we can do in any case besides wait until a restart to try again.
173    // The server will send down MIGRATION_DONE again for types needing
174    // migration as the type will still be enabled on restart.
175    SLOG(WARNING) << "Unable to migrate, configuration failed!";
176    ChangeState(IDLE);
177    to_migrate_.Clear();
178    return;
179  }
180
181  if (state_ == DISABLING_TYPES) {
182    const syncer::ModelTypeSet unsynced_types =
183        GetUnsyncedDataTypes(user_share_);
184    if (!unsynced_types.HasAll(to_migrate_)) {
185      SLOG(WARNING) << "Set of unsynced types: "
186                    << syncer::ModelTypeSetToString(unsynced_types)
187                    << " does not contain types to migrate: "
188                    << syncer::ModelTypeSetToString(to_migrate_)
189                    << "; not re-enabling yet";
190      return;
191    }
192
193    ChangeState(REENABLING_TYPES);
194    // Don't use |to_migrate_| for the re-enabling because the user
195    // may have chosen to disable types during the migration.
196    const ModelTypeSet full_set = service_->GetPreferredDataTypes();
197    SDVLOG(1) << "BackendMigrator re-enabling types: "
198              << syncer::ModelTypeSetToString(full_set);
199    manager_->Configure(full_set, syncer::CONFIGURE_REASON_MIGRATION);
200  } else if (state_ == REENABLING_TYPES) {
201    // We're done!
202    ChangeState(IDLE);
203
204    SDVLOG(1) << "BackendMigrator: Migration complete for: "
205              << syncer::ModelTypeSetToString(to_migrate_);
206    to_migrate_.Clear();
207
208    if (!migration_done_callback_.is_null())
209      migration_done_callback_.Run();
210  }
211}
212
213BackendMigrator::State BackendMigrator::state() const {
214  return state_;
215}
216
217syncer::ModelTypeSet BackendMigrator::GetPendingMigrationTypesForTest() const {
218  return to_migrate_;
219}
220
221#undef SDVLOG
222
223#undef SLOG
224
225};  // namespace browser_sync
226