Home | History | Annotate | Download | only in sync
      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/profile_sync_service_harness.h"
      6 
      7 #include <stddef.h>
      8 #include <algorithm>
      9 #include <iterator>
     10 #include <ostream>
     11 #include <set>
     12 #include <vector>
     13 
     14 #include "base/logging.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/message_loop.h"
     17 #include "base/task.h"
     18 #include "base/tracked.h"
     19 #include "chrome/browser/profiles/profile.h"
     20 #include "chrome/browser/sync/sessions/session_state.h"
     21 #include "chrome/browser/sync/signin_manager.h"
     22 
     23 using browser_sync::sessions::SyncSessionSnapshot;
     24 
     25 // The amount of time for which we wait for a live sync operation to complete.
     26 static const int kLiveSyncOperationTimeoutMs = 45000;
     27 
     28 // Simple class to implement a timeout using PostDelayedTask.  If it is not
     29 // aborted before picked up by a message queue, then it asserts with the message
     30 // provided.  This class is not thread safe.
     31 class StateChangeTimeoutEvent
     32     : public base::RefCountedThreadSafe<StateChangeTimeoutEvent> {
     33  public:
     34   StateChangeTimeoutEvent(ProfileSyncServiceHarness* caller,
     35                           const std::string& message);
     36 
     37   // The entry point to the class from PostDelayedTask.
     38   void Callback();
     39 
     40   // Cancels the actions of the callback.  Returns true if success, false
     41   // if the callback has already timed out.
     42   bool Abort();
     43 
     44  private:
     45   friend class base::RefCountedThreadSafe<StateChangeTimeoutEvent>;
     46 
     47   ~StateChangeTimeoutEvent();
     48 
     49   bool aborted_;
     50   bool did_timeout_;
     51 
     52   // Due to synchronization of the IO loop, the caller will always be alive
     53   // if the class is not aborted.
     54   ProfileSyncServiceHarness* caller_;
     55 
     56   // Informative message to assert in the case of a timeout.
     57   std::string message_;
     58 
     59   DISALLOW_COPY_AND_ASSIGN(StateChangeTimeoutEvent);
     60 };
     61 
     62 StateChangeTimeoutEvent::StateChangeTimeoutEvent(
     63     ProfileSyncServiceHarness* caller,
     64     const std::string& message)
     65     : aborted_(false), did_timeout_(false), caller_(caller), message_(message) {
     66 }
     67 
     68 StateChangeTimeoutEvent::~StateChangeTimeoutEvent() {
     69 }
     70 
     71 void StateChangeTimeoutEvent::Callback() {
     72   if (!aborted_) {
     73     if (!caller_->RunStateChangeMachine()) {
     74       // Report the message.
     75       did_timeout_ = true;
     76       DCHECK(!aborted_) << message_;
     77       caller_->SignalStateComplete();
     78     }
     79   }
     80 }
     81 
     82 bool StateChangeTimeoutEvent::Abort() {
     83   aborted_ = true;
     84   caller_ = NULL;
     85   return !did_timeout_;
     86 }
     87 
     88 ProfileSyncServiceHarness::ProfileSyncServiceHarness(
     89     Profile* profile,
     90     const std::string& username,
     91     const std::string& password,
     92     int id)
     93     : waiting_for_encryption_type_(syncable::UNSPECIFIED),
     94       wait_state_(INITIAL_WAIT_STATE),
     95       profile_(profile),
     96       service_(NULL),
     97       timestamp_match_partner_(NULL),
     98       username_(username),
     99       password_(password),
    100       id_(id) {
    101   if (IsSyncAlreadySetup()) {
    102     service_ = profile_->GetProfileSyncService();
    103     service_->AddObserver(this);
    104     wait_state_ = FULLY_SYNCED;
    105   }
    106 }
    107 
    108 // static
    109 ProfileSyncServiceHarness* ProfileSyncServiceHarness::CreateAndAttach(
    110     Profile* profile) {
    111   if (!profile->HasProfileSyncService()) {
    112     NOTREACHED() << "Profile has never signed into sync.";
    113     return NULL;
    114   }
    115   return new ProfileSyncServiceHarness(profile, "", "", 0);
    116 }
    117 
    118 void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
    119                                                const std::string& password) {
    120   username_ = username;
    121   password_ = password;
    122 }
    123 
    124 bool ProfileSyncServiceHarness::IsSyncAlreadySetup() {
    125   return profile_->HasProfileSyncService();
    126 }
    127 
    128 bool ProfileSyncServiceHarness::SetupSync() {
    129   syncable::ModelTypeSet synced_datatypes;
    130   for (int i = syncable::FIRST_REAL_MODEL_TYPE;
    131       i < syncable::MODEL_TYPE_COUNT; ++i) {
    132     synced_datatypes.insert(syncable::ModelTypeFromInt(i));
    133   }
    134   return SetupSync(synced_datatypes);
    135 }
    136 
    137 bool ProfileSyncServiceHarness::SetupSync(
    138     const syncable::ModelTypeSet& synced_datatypes) {
    139   // Initialize the sync client's profile sync service object.
    140   service_ = profile_->GetProfileSyncService("");
    141   if (service_ == NULL) {
    142     LOG(ERROR) << "SetupSync(): service_ is null.";
    143     return false;
    144   }
    145 
    146   // Subscribe sync client to notifications from the profile sync service.
    147   if (!service_->HasObserver(this))
    148     service_->AddObserver(this);
    149 
    150   // Authenticate sync client using GAIA credentials.
    151   service_->signin()->StartSignIn(username_, password_, "", "");
    152 
    153   // Wait for the OnBackendInitialized() callback.
    154   wait_state_ = WAITING_FOR_ON_BACKEND_INITIALIZED;
    155   if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs,
    156       "Waiting for OnBackendInitialized().")) {
    157     LOG(ERROR) << "OnBackendInitialized() not seen after "
    158                << kLiveSyncOperationTimeoutMs / 1000
    159                << " seconds.";
    160     return false;
    161   }
    162 
    163   // Choose the datatypes to be synced. If all datatypes are to be synced,
    164   // set sync_everything to true; otherwise, set it to false.
    165   bool sync_everything = (synced_datatypes.size() ==
    166       (syncable::MODEL_TYPE_COUNT - syncable::FIRST_REAL_MODEL_TYPE));
    167   service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
    168 
    169   // Wait for initial sync cycle to be completed.
    170   DCHECK_EQ(wait_state_, WAITING_FOR_INITIAL_SYNC);
    171   if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs,
    172       "Waiting for initial sync cycle to complete.")) {
    173     LOG(ERROR) << "Initial sync cycle did not complete after "
    174                << kLiveSyncOperationTimeoutMs / 1000
    175                << " seconds.";
    176     return false;
    177   }
    178 
    179   // Indicate to the browser that sync setup is complete.
    180   service()->SetSyncSetupCompleted();
    181 
    182   return true;
    183 }
    184 
    185 void ProfileSyncServiceHarness::SignalStateCompleteWithNextState(
    186     WaitState next_state) {
    187   wait_state_ = next_state;
    188   SignalStateComplete();
    189 }
    190 
    191 void ProfileSyncServiceHarness::SignalStateComplete() {
    192   MessageLoop::current()->Quit();
    193 }
    194 
    195 bool ProfileSyncServiceHarness::RunStateChangeMachine() {
    196   WaitState original_wait_state = wait_state_;
    197   switch (wait_state_) {
    198     case WAITING_FOR_ON_BACKEND_INITIALIZED: {
    199       LogClientInfo("WAITING_FOR_ON_BACKEND_INITIALIZED");
    200       if (service()->sync_initialized()) {
    201         // The sync backend is initialized.
    202         SignalStateCompleteWithNextState(WAITING_FOR_INITIAL_SYNC);
    203       }
    204       break;
    205     }
    206     case WAITING_FOR_INITIAL_SYNC: {
    207       LogClientInfo("WAITING_FOR_INITIAL_SYNC");
    208       if (IsSynced()) {
    209         // The first sync cycle is now complete. We can start running tests.
    210         SignalStateCompleteWithNextState(FULLY_SYNCED);
    211       }
    212       break;
    213     }
    214     case WAITING_FOR_SYNC_TO_FINISH: {
    215       LogClientInfo("WAITING_FOR_SYNC_TO_FINISH");
    216       if (!IsSynced()) {
    217         // The client is not yet fully synced. Continue waiting.
    218         if (!GetStatus().server_reachable) {
    219           // The client cannot reach the sync server because the network is
    220           // disabled. There is no need to wait anymore.
    221           SignalStateCompleteWithNextState(SERVER_UNREACHABLE);
    222         }
    223         break;
    224       }
    225       SignalStateCompleteWithNextState(FULLY_SYNCED);
    226       break;
    227     }
    228     case WAITING_FOR_UPDATES: {
    229       LogClientInfo("WAITING_FOR_UPDATES");
    230       DCHECK(timestamp_match_partner_);
    231       if (!MatchesOtherClient(timestamp_match_partner_)) {
    232         // The client is not yet fully synced; keep waiting until we converge.
    233         break;
    234       }
    235       timestamp_match_partner_->service()->RemoveObserver(this);
    236       timestamp_match_partner_ = NULL;
    237 
    238       SignalStateCompleteWithNextState(FULLY_SYNCED);
    239       break;
    240     }
    241     case WAITING_FOR_PASSPHRASE_ACCEPTED: {
    242       LogClientInfo("WAITING_FOR_PASSPHRASE_ACCEPTED");
    243       // TODO(atwilson): After ProfileSyncService::OnPassphraseAccepted() is
    244       // fixed, add an extra check to make sure that the value of
    245       // service()->observed_passphrase_required() is false.
    246       if (service()->ShouldPushChanges()) {
    247         // The passphrase has been accepted, and sync has been restarted.
    248         SignalStateCompleteWithNextState(FULLY_SYNCED);
    249       }
    250       break;
    251     }
    252     case WAITING_FOR_ENCRYPTION: {
    253       LogClientInfo("WAITING_FOR_ENCRYPTION");
    254       if (IsTypeEncrypted(waiting_for_encryption_type_)) {
    255         // Encryption is complete for the type we are waiting on.
    256         SignalStateCompleteWithNextState(FULLY_SYNCED);
    257       }
    258       break;
    259     }
    260     case SERVER_UNREACHABLE: {
    261       LogClientInfo("SERVER_UNREACHABLE");
    262       if (GetStatus().server_reachable) {
    263         // The client was offline due to the network being disabled, but is now
    264         // back online. Wait for the pending sync cycle to complete.
    265         SignalStateCompleteWithNextState(WAITING_FOR_SYNC_TO_FINISH);
    266       }
    267       break;
    268     }
    269     case FULLY_SYNCED: {
    270       // The client is online and fully synced. There is nothing to do.
    271       LogClientInfo("FULLY_SYNCED");
    272       break;
    273     }
    274     case SYNC_DISABLED: {
    275       // Syncing is disabled for the client. There is nothing to do.
    276       LogClientInfo("SYNC_DISABLED");
    277       break;
    278     }
    279     default:
    280       // Invalid state during observer callback which may be triggered by other
    281       // classes using the the UI message loop.  Defer to their handling.
    282       break;
    283   }
    284   return original_wait_state != wait_state_;
    285 }
    286 
    287 void ProfileSyncServiceHarness::OnStateChanged() {
    288   RunStateChangeMachine();
    289 }
    290 
    291 bool ProfileSyncServiceHarness::AwaitPassphraseAccepted() {
    292   LogClientInfo("AwaitPassphraseAccepted");
    293   if (wait_state_ == SYNC_DISABLED) {
    294     LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
    295     return false;
    296   }
    297 
    298   // TODO(atwilson): After ProfileSyncService::OnPassphraseAccepted() is
    299   // fixed, add an extra check to make sure that the value of
    300   // service()->observed_passphrase_required() is false.
    301   if (service()->ShouldPushChanges()) {
    302     // Passphrase is already accepted; don't wait.
    303     return true;
    304   }
    305 
    306   wait_state_ = WAITING_FOR_PASSPHRASE_ACCEPTED;
    307   return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs,
    308                                       "Waiting for passphrase accepted.");
    309 }
    310 
    311 bool ProfileSyncServiceHarness::AwaitSyncCycleCompletion(
    312     const std::string& reason) {
    313   LogClientInfo("AwaitSyncCycleCompletion");
    314   if (wait_state_ == SYNC_DISABLED) {
    315     LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
    316     return false;
    317   }
    318 
    319   if (IsSynced()) {
    320     // Client is already synced; don't wait.
    321     return true;
    322   }
    323 
    324   if (wait_state_ == SERVER_UNREACHABLE) {
    325     // Client was offline; wait for it to go online, and then wait for sync.
    326     AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
    327     DCHECK_EQ(wait_state_, WAITING_FOR_SYNC_TO_FINISH);
    328     return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
    329   } else {
    330     DCHECK(service()->sync_initialized());
    331     wait_state_ = WAITING_FOR_SYNC_TO_FINISH;
    332     AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
    333     if (wait_state_ == FULLY_SYNCED) {
    334       // Client is online; sync was successful.
    335       return true;
    336     } else if (wait_state_ == SERVER_UNREACHABLE) {
    337       // Client is offline; sync was unsuccessful.
    338       return false;
    339     } else {
    340       LOG(ERROR) << "Invalid wait state:" << wait_state_;
    341       return false;
    342     }
    343   }
    344 }
    345 
    346 bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
    347     ProfileSyncServiceHarness* partner) {
    348   LogClientInfo("AwaitMutualSyncCycleCompletion");
    349   if (!AwaitSyncCycleCompletion("Sync cycle completion on active client."))
    350     return false;
    351   return partner->WaitUntilTimestampMatches(this,
    352       "Sync cycle completion on passive client.");
    353 }
    354 
    355 bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
    356     std::vector<ProfileSyncServiceHarness*>& partners) {
    357   LogClientInfo("AwaitGroupSyncCycleCompletion");
    358   if (!AwaitSyncCycleCompletion("Sync cycle completion on active client."))
    359     return false;
    360   bool return_value = true;
    361   for (std::vector<ProfileSyncServiceHarness*>::iterator it =
    362       partners.begin(); it != partners.end(); ++it) {
    363     if ((this != *it) && ((*it)->wait_state_ != SYNC_DISABLED)) {
    364       return_value = return_value &&
    365           (*it)->WaitUntilTimestampMatches(this,
    366           "Sync cycle completion on partner client.");
    367     }
    368   }
    369   return return_value;
    370 }
    371 
    372 // static
    373 bool ProfileSyncServiceHarness::AwaitQuiescence(
    374     std::vector<ProfileSyncServiceHarness*>& clients) {
    375   VLOG(1) << "AwaitQuiescence.";
    376   bool return_value = true;
    377   for (std::vector<ProfileSyncServiceHarness*>::iterator it =
    378       clients.begin(); it != clients.end(); ++it) {
    379     if ((*it)->wait_state_ != SYNC_DISABLED)
    380       return_value = return_value &&
    381           (*it)->AwaitGroupSyncCycleCompletion(clients);
    382   }
    383   return return_value;
    384 }
    385 
    386 bool ProfileSyncServiceHarness::WaitUntilTimestampMatches(
    387     ProfileSyncServiceHarness* partner, const std::string& reason) {
    388   LogClientInfo("WaitUntilTimestampMatches");
    389   if (wait_state_ == SYNC_DISABLED) {
    390     LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
    391     return false;
    392   }
    393 
    394   if (MatchesOtherClient(partner)) {
    395     // Timestamps already match; don't wait.
    396     return true;
    397   }
    398 
    399   DCHECK(!timestamp_match_partner_);
    400   timestamp_match_partner_ = partner;
    401   partner->service()->AddObserver(this);
    402   wait_state_ = WAITING_FOR_UPDATES;
    403   return AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason);
    404 }
    405 
    406 bool ProfileSyncServiceHarness::AwaitStatusChangeWithTimeout(
    407     int timeout_milliseconds,
    408     const std::string& reason) {
    409   LogClientInfo("AwaitStatusChangeWithTimeout");
    410   if (wait_state_ == SYNC_DISABLED) {
    411     LOG(ERROR) << "Sync disabled for Client " << id_ << ".";
    412     return false;
    413   }
    414   scoped_refptr<StateChangeTimeoutEvent> timeout_signal(
    415       new StateChangeTimeoutEvent(this, reason));
    416   MessageLoop* loop = MessageLoop::current();
    417   bool did_allow_nestable_tasks = loop->NestableTasksAllowed();
    418   loop->SetNestableTasksAllowed(true);
    419   loop->PostDelayedTask(
    420       FROM_HERE,
    421       NewRunnableMethod(timeout_signal.get(),
    422                         &StateChangeTimeoutEvent::Callback),
    423       timeout_milliseconds);
    424   loop->Run();
    425   loop->SetNestableTasksAllowed(did_allow_nestable_tasks);
    426   if (timeout_signal->Abort()) {
    427     LogClientInfo("AwaitStatusChangeWithTimeout succeeded");
    428     return true;
    429   } else {
    430     LogClientInfo("AwaitStatusChangeWithTimeout timed out");
    431     return false;
    432   }
    433 }
    434 
    435 ProfileSyncService::Status ProfileSyncServiceHarness::GetStatus() {
    436   DCHECK(service() != NULL) << "GetStatus(): service() is NULL.";
    437   return service()->QueryDetailedSyncStatus();
    438 }
    439 
    440 bool ProfileSyncServiceHarness::IsSynced() {
    441   LogClientInfo("IsSynced");
    442   if (service() == NULL)
    443     return false;
    444   const SyncSessionSnapshot* snap = GetLastSessionSnapshot();
    445   // TODO(rsimha): Remove additional checks of snap->has_more_to_sync and
    446   // snap->unsynced_count once http://crbug.com/48989 is fixed.
    447   return (snap &&
    448           snap->num_conflicting_updates == 0 &&  // We can decrypt everything.
    449           ServiceIsPushingChanges() &&
    450           GetStatus().notifications_enabled &&
    451           !service()->HasUnsyncedItems() &&
    452           !snap->has_more_to_sync &&
    453           snap->unsynced_count == 0);
    454 }
    455 
    456 bool ProfileSyncServiceHarness::MatchesOtherClient(
    457     ProfileSyncServiceHarness* partner) {
    458   if (!IsSynced())
    459     return false;
    460 
    461   // Only look for a match if we have at least one enabled datatype in
    462   // common with the partner client.
    463   syncable::ModelTypeSet types, other_types, intersection_types;
    464   service()->GetPreferredDataTypes(&types);
    465   partner->service()->GetPreferredDataTypes(&other_types);
    466   std::set_intersection(types.begin(), types.end(), other_types.begin(),
    467                         other_types.end(),
    468                         inserter(intersection_types,
    469                                  intersection_types.begin()));
    470   for (syncable::ModelTypeSet::iterator i = intersection_types.begin();
    471        i != intersection_types.end();
    472        ++i) {
    473     if (!partner->IsSynced() ||
    474         partner->GetUpdatedTimestamp(*i) != GetUpdatedTimestamp(*i)) {
    475       return false;
    476     }
    477   }
    478   return true;
    479 }
    480 
    481 const SyncSessionSnapshot*
    482     ProfileSyncServiceHarness::GetLastSessionSnapshot() const {
    483   DCHECK(service_ != NULL) << "Sync service has not yet been set up.";
    484   if (service_->sync_initialized()) {
    485     return service_->GetLastSessionSnapshot();
    486   }
    487   return NULL;
    488 }
    489 
    490 void ProfileSyncServiceHarness::EnableSyncForDatatype(
    491     syncable::ModelType datatype) {
    492   LogClientInfo("EnableSyncForDatatype");
    493   syncable::ModelTypeSet synced_datatypes;
    494   if (wait_state_ == SYNC_DISABLED) {
    495     wait_state_ = WAITING_FOR_ON_BACKEND_INITIALIZED;
    496     synced_datatypes.insert(datatype);
    497     DCHECK(SetupSync(synced_datatypes)) << "Reinitialization of Client " << id_
    498                                         << " failed.";
    499   } else {
    500     DCHECK(service() != NULL) << "EnableSyncForDatatype(): service() is null.";
    501     service()->GetPreferredDataTypes(&synced_datatypes);
    502     syncable::ModelTypeSet::iterator it = synced_datatypes.find(
    503         syncable::ModelTypeFromInt(datatype));
    504     if (it == synced_datatypes.end()) {
    505       synced_datatypes.insert(syncable::ModelTypeFromInt(datatype));
    506       service()->OnUserChoseDatatypes(false, synced_datatypes);
    507       wait_state_ = WAITING_FOR_SYNC_TO_FINISH;
    508       AwaitSyncCycleCompletion("Waiting for datatype configuration.");
    509       VLOG(1) << "EnableSyncForDatatype(): Enabled sync for datatype "
    510               << syncable::ModelTypeToString(datatype) << " on Client " << id_;
    511     } else {
    512       VLOG(1) << "EnableSyncForDatatype(): Sync already enabled for datatype "
    513               << syncable::ModelTypeToString(datatype) << " on Client " << id_;
    514     }
    515   }
    516 }
    517 
    518 void ProfileSyncServiceHarness::DisableSyncForDatatype(
    519     syncable::ModelType datatype) {
    520   LogClientInfo("DisableSyncForDatatype");
    521   syncable::ModelTypeSet synced_datatypes;
    522   DCHECK(service() != NULL) << "DisableSyncForDatatype(): service() is null.";
    523   service()->GetPreferredDataTypes(&synced_datatypes);
    524   syncable::ModelTypeSet::iterator it = synced_datatypes.find(datatype);
    525   if (it != synced_datatypes.end()) {
    526     synced_datatypes.erase(it);
    527     service()->OnUserChoseDatatypes(false, synced_datatypes);
    528     AwaitSyncCycleCompletion("Waiting for datatype configuration.");
    529     VLOG(1) << "DisableSyncForDatatype(): Disabled sync for datatype "
    530             << syncable::ModelTypeToString(datatype) << " on Client " << id_;
    531   } else {
    532     VLOG(1) << "DisableSyncForDatatype(): Sync already disabled for datatype "
    533             << syncable::ModelTypeToString(datatype) << " on Client " << id_;
    534   }
    535 }
    536 
    537 void ProfileSyncServiceHarness::EnableSyncForAllDatatypes() {
    538   LogClientInfo("EnableSyncForAllDatatypes");
    539   if (wait_state_ == SYNC_DISABLED) {
    540     wait_state_ = WAITING_FOR_ON_BACKEND_INITIALIZED;
    541     DCHECK(SetupSync()) << "Reinitialization of Client " << id_ << " failed.";
    542   } else {
    543     syncable::ModelTypeSet synced_datatypes;
    544     for (int i = syncable::FIRST_REAL_MODEL_TYPE;
    545         i < syncable::MODEL_TYPE_COUNT; ++i) {
    546       synced_datatypes.insert(syncable::ModelTypeFromInt(i));
    547     }
    548     DCHECK(service() != NULL) << "EnableSyncForAllDatatypes(): service() is "
    549                                  " null.";
    550     service()->OnUserChoseDatatypes(true, synced_datatypes);
    551     wait_state_ = WAITING_FOR_SYNC_TO_FINISH;
    552     AwaitSyncCycleCompletion("Waiting for datatype configuration.");
    553     VLOG(1) << "EnableSyncForAllDatatypes(): Enabled sync for all datatypes on "
    554                "Client " << id_;
    555   }
    556 }
    557 
    558 void ProfileSyncServiceHarness::DisableSyncForAllDatatypes() {
    559   LogClientInfo("DisableSyncForAllDatatypes");
    560   DCHECK(service() != NULL) << "EnableSyncForAllDatatypes(): service() is "
    561                                "null.";
    562   service()->DisableForUser();
    563   wait_state_ = SYNC_DISABLED;
    564   VLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all datatypes on "
    565              "Client " << id_;
    566 }
    567 
    568 std::string ProfileSyncServiceHarness::GetUpdatedTimestamp(
    569     syncable::ModelType model_type) {
    570   const SyncSessionSnapshot* snap = GetLastSessionSnapshot();
    571   DCHECK(snap != NULL) << "GetUpdatedTimestamp(): Sync snapshot is NULL.";
    572   return snap->download_progress_markers[model_type];
    573 }
    574 
    575 void ProfileSyncServiceHarness::LogClientInfo(const std::string& message) {
    576   if (service()) {
    577     const SyncSessionSnapshot* snap = GetLastSessionSnapshot();
    578     if (snap) {
    579       VLOG(1) << "Client " << id_ << ": " << message
    580               << ": num_updates_downloaded : "
    581               << snap->syncer_status.num_updates_downloaded_total
    582               << ", has_more_to_sync: " << snap->has_more_to_sync
    583               << ", unsynced_count: " << snap->unsynced_count
    584               << ", num_conflicting_updates: " << snap->num_conflicting_updates
    585               << ", has_unsynced_items: "
    586               << service()->HasUnsyncedItems()
    587               << ", observed_passphrase_required: "
    588               << service()->observed_passphrase_required()
    589               << ", notifications_enabled: "
    590               << GetStatus().notifications_enabled
    591               << ", service_is_pushing_changes: " << ServiceIsPushingChanges();
    592     } else {
    593       VLOG(1) << "Client " << id_ << ": " << message
    594               << ": Sync session snapshot not available.";
    595     }
    596   } else {
    597     VLOG(1) << "Client " << id_ << ": " << message
    598             << ": Sync service not available.";
    599   }
    600 }
    601 
    602 bool ProfileSyncServiceHarness::EnableEncryptionForType(
    603     syncable::ModelType type) {
    604   syncable::ModelTypeSet encrypted_types;
    605   service_->GetEncryptedDataTypes(&encrypted_types);
    606   if (encrypted_types.count(type) > 0)
    607     return true;
    608   encrypted_types.insert(type);
    609   service_->EncryptDataTypes(encrypted_types);
    610 
    611   // Wait some time to let the enryption finish.
    612   std::string reason = "Waiting for encryption.";
    613   DCHECK_EQ(FULLY_SYNCED, wait_state_);
    614   wait_state_ = WAITING_FOR_ENCRYPTION;
    615   waiting_for_encryption_type_ = type;
    616   if (!AwaitStatusChangeWithTimeout(kLiveSyncOperationTimeoutMs, reason)) {
    617     LOG(ERROR) << "Did not receive EncryptionComplete notification after"
    618                << kLiveSyncOperationTimeoutMs / 1000
    619                << " seconds.";
    620     return false;
    621   }
    622 
    623   return IsTypeEncrypted(type);
    624 }
    625 
    626 bool ProfileSyncServiceHarness::IsTypeEncrypted(syncable::ModelType type) {
    627   syncable::ModelTypeSet encrypted_types;
    628   service_->GetEncryptedDataTypes(&encrypted_types);
    629   if (encrypted_types.count(type) == 0) {
    630     return false;
    631   }
    632   return true;
    633 }
    634