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