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/backend_migrator.h"
6
7 #include <algorithm>
8
9 #include "base/string_number_conversions.h"
10 #include "chrome/browser/sync/profile_sync_service.h"
11 #include "chrome/browser/sync/glue/data_type_manager.h"
12 #include "chrome/browser/sync/sessions/session_state.h"
13 #include "content/browser/browser_thread.h"
14 #include "content/common/notification_details.h"
15 #include "content/common/notification_source.h"
16
17 using syncable::ModelTypeSet;
18
19 namespace browser_sync {
20
21 using sessions::SyncSessionSnapshot;
22
BackendMigrator(ProfileSyncService * service,DataTypeManager * manager)23 BackendMigrator::BackendMigrator(ProfileSyncService* service,
24 DataTypeManager* manager)
25 : state_(IDLE), service_(service), manager_(manager),
26 restart_migration_(false),
27 method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
28 registrar_.Add(this, NotificationType::SYNC_CONFIGURE_DONE,
29 Source<DataTypeManager>(manager_));
30 service_->AddObserver(this);
31 }
32
~BackendMigrator()33 BackendMigrator::~BackendMigrator() {
34 service_->RemoveObserver(this);
35 }
36
HasStartedMigrating() const37 bool BackendMigrator::HasStartedMigrating() const {
38 return state_ >= DISABLING_TYPES;
39 }
40
MigrateTypes(const syncable::ModelTypeSet & types)41 void BackendMigrator::MigrateTypes(const syncable::ModelTypeSet& types) {
42 {
43 ModelTypeSet temp;
44 std::set_union(to_migrate_.begin(), to_migrate_.end(),
45 types.begin(), types.end(),
46 std::inserter(temp, temp.end()));
47 to_migrate_ = temp;
48 }
49
50 if (HasStartedMigrating()) {
51 VLOG(1) << "BackendMigrator::MigrateTypes: STARTED_MIGRATING early-out.";
52 restart_migration_ = true;
53 return;
54 }
55
56 if (manager_->state() != DataTypeManager::CONFIGURED) {
57 VLOG(1) << "BackendMigrator::MigrateTypes: manager CONFIGURED early-out.";
58 state_ = WAITING_TO_START;
59 return;
60 }
61
62 // We'll now disable any running types that need to be migrated.
63 state_ = DISABLING_TYPES;
64 ModelTypeSet full_set;
65 service_->GetPreferredDataTypes(&full_set);
66 ModelTypeSet difference;
67 std::set_difference(full_set.begin(), full_set.end(),
68 to_migrate_.begin(), to_migrate_.end(),
69 std::inserter(difference, difference.end()));
70 VLOG(1) << "BackendMigrator disabling types; calling Configure.";
71 manager_->Configure(difference);
72 }
73
OnStateChanged()74 void BackendMigrator::OnStateChanged() {
75 if (restart_migration_ == true) {
76 VLOG(1) << "BackendMigrator restarting migration in OnStateChanged.";
77 state_ = WAITING_TO_START;
78 restart_migration_ = false;
79 MigrateTypes(to_migrate_);
80 return;
81 }
82
83 if (state_ != WAITING_FOR_PURGE)
84 return;
85
86 size_t num_empty_migrated_markers = 0;
87 const SyncSessionSnapshot* snap = service_->GetLastSessionSnapshot();
88 for (ModelTypeSet::const_iterator it = to_migrate_.begin();
89 it != to_migrate_.end(); ++it) {
90 if (snap->download_progress_markers[*it].empty())
91 num_empty_migrated_markers++;
92 }
93
94 if (num_empty_migrated_markers < to_migrate_.size())
95 return;
96
97 state_ = REENABLING_TYPES;
98 ModelTypeSet full_set;
99 service_->GetPreferredDataTypes(&full_set);
100 VLOG(1) << "BackendMigrator re-enabling types.";
101 // Don't use |to_migrate_| for the re-enabling because the user may have
102 // chosen to disable types during the migration.
103 manager_->Configure(full_set);
104 }
105
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)106 void BackendMigrator::Observe(NotificationType type,
107 const NotificationSource& source,
108 const NotificationDetails& details) {
109 DCHECK_EQ(NotificationType::SYNC_CONFIGURE_DONE, type.value);
110 if (state_ == IDLE)
111 return;
112
113 DataTypeManager::ConfigureResultWithErrorLocation* result =
114 Details<DataTypeManager::ConfigureResultWithErrorLocation>(
115 details).ptr();
116
117 ModelTypeSet intersection;
118 std::set_intersection(result->requested_types.begin(),
119 result->requested_types.end(), to_migrate_.begin(), to_migrate_.end(),
120 std::inserter(intersection, intersection.end()));
121
122 // The intersection check is to determine if our disable request was
123 // interrupted by a user changing preferred types. May still need to purge.
124 // It's pretty wild if we're in WAITING_FOR_PURGE here, because it would mean
125 // that after our disable-config finished but before the purge, another config
126 // was posted externally _and completed_, which means somehow the nudge to
127 // purge was dropped, yet nudges are reliable.
128 if (state_ == WAITING_TO_START || state_ == WAITING_FOR_PURGE ||
129 (state_ == DISABLING_TYPES && !intersection.empty())) {
130 state_ = WAITING_TO_START;
131 restart_migration_ = false;
132 VLOG(1) << "BackendMigrator::Observe posting MigrateTypes.";
133 if (!BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
134 method_factory_.NewRunnableMethod(&BackendMigrator::MigrateTypes,
135 to_migrate_))) {
136 // Unittests need this.
137 // TODO(tim): Clean this up.
138 MigrateTypes(to_migrate_);
139 }
140 return;
141 }
142
143 if (result->result != DataTypeManager::OK) {
144 // If this fails, and we're disabling types, a type may or may not be
145 // disabled until the user restarts the browser. If this wasn't an abort,
146 // any failure will be reported as an unrecoverable error to the UI. If it
147 // was an abort, then typically things are shutting down anyway. There isn't
148 // much we can do in any case besides wait until a restart to try again.
149 // The server will send down MIGRATION_DONE again for types needing
150 // migration as the type will still be enabled on restart.
151 LOG(WARNING) << "Unable to migrate, configuration failed!";
152 state_ = IDLE;
153 to_migrate_.clear();
154 return;
155 }
156
157 if (state_ == DISABLING_TYPES) {
158 state_ = WAITING_FOR_PURGE;
159 VLOG(1) << "BackendMigrator waiting for purge.";
160 } else if (state_ == REENABLING_TYPES) {
161 // We're done!
162 state_ = IDLE;
163
164 std::stringstream ss;
165 std::copy(to_migrate_.begin(), to_migrate_.end(),
166 std::ostream_iterator<syncable::ModelType>(ss, ","));
167 VLOG(1) << "BackendMigrator: Migration complete for: " << ss.str();
168 to_migrate_.clear();
169 }
170 }
171
state() const172 BackendMigrator::State BackendMigrator::state() const {
173 return state_;
174 }
175
176 }; // namespace browser_sync
177