profile_sync_service_harness.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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
23using browser_sync::sessions::SyncSessionSnapshot;
24
25// The amount of time for which we wait for a live sync operation to complete.
26static 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.
31class 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
62StateChangeTimeoutEvent::StateChangeTimeoutEvent(
63    ProfileSyncServiceHarness* caller,
64    const std::string& message)
65    : aborted_(false), did_timeout_(false), caller_(caller), message_(message) {
66}
67
68StateChangeTimeoutEvent::~StateChangeTimeoutEvent() {
69}
70
71void 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
82bool StateChangeTimeoutEvent::Abort() {
83  aborted_ = true;
84  caller_ = NULL;
85  return !did_timeout_;
86}
87
88ProfileSyncServiceHarness::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
109ProfileSyncServiceHarness* 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
118void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
119                                               const std::string& password) {
120  username_ = username;
121  password_ = password;
122}
123
124bool ProfileSyncServiceHarness::IsSyncAlreadySetup() {
125  return profile_->HasProfileSyncService();
126}
127
128bool 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
137bool 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
185void ProfileSyncServiceHarness::SignalStateCompleteWithNextState(
186    WaitState next_state) {
187  wait_state_ = next_state;
188  SignalStateComplete();
189}
190
191void ProfileSyncServiceHarness::SignalStateComplete() {
192  MessageLoop::current()->Quit();
193}
194
195bool 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
287void ProfileSyncServiceHarness::OnStateChanged() {
288  RunStateChangeMachine();
289}
290
291bool 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
311bool 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
346bool 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
355bool 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
373bool 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
386bool 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
406bool 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
435ProfileSyncService::Status ProfileSyncServiceHarness::GetStatus() {
436  DCHECK(service() != NULL) << "GetStatus(): service() is NULL.";
437  return service()->QueryDetailedSyncStatus();
438}
439
440bool 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
456bool 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
481const 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
490void 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
518void 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
537void 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
558void 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
568std::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
575void 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
602bool 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
626bool 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