Home | History | Annotate | Download | only in sync
      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 
     25 MigrationObserver::~MigrationObserver() {}
     26 
     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       weak_ptr_factory_(this),
     35       migration_done_callback_(migration_done_callback) {
     36 }
     37 
     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 
     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 
     73 void BackendMigrator::AddMigrationObserver(MigrationObserver* observer) {
     74   migration_observers_.AddObserver(observer);
     75 }
     76 
     77 bool BackendMigrator::HasMigrationObserver(
     78     MigrationObserver* observer) const {
     79   return migration_observers_.HasObserver(observer);
     80 }
     81 
     82 void BackendMigrator::RemoveMigrationObserver(MigrationObserver* observer) {
     83   migration_observers_.RemoveObserver(observer);
     84 }
     85 
     86 void BackendMigrator::ChangeState(State new_state) {
     87   state_ = new_state;
     88   FOR_EACH_OBSERVER(MigrationObserver, migration_observers_,
     89                     OnMigrationStateChange());
     90 }
     91 
     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 
    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 
    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 
    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 
    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 
    213 BackendMigrator::State BackendMigrator::state() const {
    214   return state_;
    215 }
    216 
    217 syncer::ModelTypeSet BackendMigrator::GetPendingMigrationTypesForTest() const {
    218   return to_migrate_;
    219 }
    220 
    221 #undef SDVLOG
    222 
    223 #undef SLOG
    224 
    225 };  // namespace browser_sync
    226