• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
19 using syncer::ModelTypeSet;
20 
21 namespace browser_sync {
22 
23 using syncer::ModelTypeToString;
24 
~MigrationObserver()25 MigrationObserver::~MigrationObserver() {}
26 
BackendMigrator(const std::string & name,syncer::UserShare * user_share,ProfileSyncService * service,DataTypeManager * manager,const base::Closure & migration_done_callback)27 BackendMigrator::BackendMigrator(const std::string& name,
28                                  syncer::UserShare* user_share,
29                                  ProfileSyncService* service,
30                                  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 
~BackendMigrator()38 BackendMigrator::~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 
MigrateTypes(syncer::ModelTypeSet types)48 void 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 
AddMigrationObserver(MigrationObserver * observer)73 void BackendMigrator::AddMigrationObserver(MigrationObserver* observer) {
74   migration_observers_.AddObserver(observer);
75 }
76 
HasMigrationObserver(MigrationObserver * observer) const77 bool BackendMigrator::HasMigrationObserver(
78     MigrationObserver* observer) const {
79   return migration_observers_.HasObserver(observer);
80 }
81 
RemoveMigrationObserver(MigrationObserver * observer)82 void BackendMigrator::RemoveMigrationObserver(MigrationObserver* observer) {
83   migration_observers_.RemoveObserver(observer);
84 }
85 
ChangeState(State new_state)86 void BackendMigrator::ChangeState(State new_state) {
87   state_ = new_state;
88   FOR_EACH_OBSERVER(MigrationObserver, migration_observers_,
89                     OnMigrationStateChange());
90 }
91 
TryStart()92 bool BackendMigrator::TryStart() {
93   DCHECK_EQ(state_, WAITING_TO_START);
94   if (manager_->state() == DataTypeManager::CONFIGURED) {
95     RestartMigration();
96     return true;
97   }
98   return false;
99 }
100 
RestartMigration()101 void 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 
OnConfigureDone(const DataTypeManager::ConfigureResult & result)110 void BackendMigrator::OnConfigureDone(
111     const 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 
124 namespace {
125 
GetUnsyncedDataTypes(syncer::UserShare * user_share)126 syncer::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 
OnConfigureDoneImpl(const DataTypeManager::ConfigureResult & result)143 void BackendMigrator::OnConfigureDoneImpl(
144     const 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 != 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 
state() const213 BackendMigrator::State BackendMigrator::state() const {
214   return state_;
215 }
216 
GetPendingMigrationTypesForTest() const217 syncer::ModelTypeSet BackendMigrator::GetPendingMigrationTypesForTest() const {
218   return to_migrate_;
219 }
220 
221 #undef SDVLOG
222 
223 #undef SLOG
224 
225 };  // namespace browser_sync
226