[Sync] Add keystore encryption info to about:sync

This patch adds the following fields under the encryption section of about:sync
- Has Keystore Key: whether the encryption handler has a keystore encryption key
- Migration Time: the time migration was performed, or "Not Migrated" if
  migration hasn't been performed yet
- Passphrase Type: the actual passphrase type (provides more detail than
   Is Using Explicit Passphrase, but stored at a diff layer)

Added sync/api/time.h, which just includes sync/util/time.h but is accessible
from chrome/

BUG=129665


Review URL: https://chromiumcodereview.appspot.com/10917246

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@157499 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/sync/about_sync_util.cc b/chrome/browser/sync/about_sync_util.cc
index c0487ad9..5138a3c 100644
--- a/chrome/browser/sync/about_sync_util.cc
+++ b/chrome/browser/sync/about_sync_util.cc
@@ -11,6 +11,8 @@
 #include "chrome/browser/signin/signin_manager.h"
 #include "chrome/browser/sync/profile_sync_service.h"
 #include "chrome/common/chrome_version_info.h"
+#include "sync/api/time.h"
+#include "sync/internal_api/public/util/sync_string_conversions.h"
 #include "sync/protocol/proto_enum_conversions.h"
 
 using base::DictionaryValue;
@@ -141,6 +143,15 @@
       version_modifier;
 }
 
+std::string GetKeystoreMigrationTimeStr(base::Time migration_time) {
+  std::string migration_time_str;
+  if (migration_time.is_null())
+    migration_time_str = "Not Migrated";
+  else
+    migration_time_str = syncer::GetTimeDebugString(migration_time);
+  return migration_time_str;
+}
+
 }  // namespace
 
 namespace sync_ui_util {
@@ -193,6 +204,11 @@
   BoolSyncStat has_pending_keys(section_encryption,
                                 "Cryptographer Has Pending Keys");
   StringSyncStat encrypted_types(section_encryption, "Encrypted Types");
+  BoolSyncStat has_keystore_key(section_encryption, "Has Keystore Key");
+  StringSyncStat keystore_migration_time(section_encryption,
+                                         "Keystore Migration Time");
+  StringSyncStat passphrase_type(section_encryption,
+                                 "Passphrase Type");
 
   ListValue* section_last_session = AddSection(
       stats_list, "Status from Last Completed Session");
@@ -293,6 +309,11 @@
     has_pending_keys.SetValue(full_status.crypto_has_pending_keys);
     encrypted_types.SetValue(
         ModelTypeSetToString(full_status.encrypted_types));
+    has_keystore_key.SetValue(full_status.has_keystore_key);
+    keystore_migration_time.SetValue(
+        GetKeystoreMigrationTimeStr(full_status.keystore_migration_time));
+    passphrase_type.SetValue(
+        PassphraseTypeToString(full_status.passphrase_type));
   }
 
   if (snapshot.is_initialized()) {
diff --git a/chrome/browser/sync/glue/session_model_associator.cc b/chrome/browser/sync/glue/session_model_associator.cc
index 92e7557..38c71b7 100644
--- a/chrome/browser/sync/glue/session_model_associator.cc
+++ b/chrome/browser/sync/glue/session_model_associator.cc
@@ -30,6 +30,7 @@
 #include "content/public/browser/notification_details.h"
 #include "content/public/browser/notification_service.h"
 #include "sync/api/sync_error.h"
+#include "sync/api/time.h"
 #include "sync/internal_api/public/base/model_type.h"
 #include "sync/internal_api/public/base/model_type_state_map.h"
 #include "sync/internal_api/public/read_node.h"
@@ -41,7 +42,6 @@
 #include "sync/syncable/read_transaction.h"
 #include "sync/syncable/write_transaction.h"
 #include "sync/util/get_session_name.h"
-#include "sync/util/time.h"
 #include "ui/gfx/favicon_size.h"
 #if defined(OS_LINUX)
 #include "base/linux_util.h"
diff --git a/sync/api/DEPS b/sync/api/DEPS
index f6dca6a8..c3b8bac 100644
--- a/sync/api/DEPS
+++ b/sync/api/DEPS
@@ -2,5 +2,5 @@
   "+sync/internal_api/base_node.h",
   "+sync/internal_api/public",
   "+sync/protocol",
-  "+sync/util/immutable.h",
+  "+sync/util",
 ]
diff --git a/sync/api/time.h b/sync/api/time.h
new file mode 100644
index 0000000..5df108b
--- /dev/null
+++ b/sync/api/time.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Publicly accessible header for time-related sync functions.
+// See sync/util/time.h for implemenation.
+
+#ifndef SYNC_API_TIME_H_
+#define SYNC_API_TIME_H_
+
+#include "sync/util/time.h"
+
+#endif  // SYNC_API_TIME_H_
diff --git a/sync/engine/all_status.cc b/sync/engine/all_status.cc
index 3df6c29..656d6e0b 100644
--- a/sync/engine/all_status.cc
+++ b/sync/engine/all_status.cc
@@ -154,6 +154,21 @@
   status_.crypto_has_pending_keys = has_pending_keys;
 }
 
+void AllStatus::SetPassphraseType(PassphraseType type) {
+  ScopedStatusLock lock(this);
+  status_.passphrase_type = type;
+}
+
+void AllStatus::SetHasKeystoreKey(bool has_keystore_key) {
+  ScopedStatusLock lock(this);
+  status_.has_keystore_key = has_keystore_key;
+}
+
+void AllStatus::SetKeystoreMigrationTime(const base::Time& migration_time) {
+  ScopedStatusLock lock(this);
+  status_.keystore_migration_time = migration_time;
+}
+
 void AllStatus::SetUniqueId(const std::string& guid) {
   ScopedStatusLock lock(this);
   status_.unique_id = guid;
diff --git a/sync/engine/all_status.h b/sync/engine/all_status.h
index e350f57c..9649a35f 100644
--- a/sync/engine/all_status.h
+++ b/sync/engine/all_status.h
@@ -53,6 +53,9 @@
   void SetEncryptedTypes(ModelTypeSet types);
   void SetCryptographerReady(bool ready);
   void SetCryptoHasPendingKeys(bool has_pending_keys);
+  void SetPassphraseType(PassphraseType type);
+  void SetHasKeystoreKey(bool has_keystore_key);
+  void SetKeystoreMigrationTime(const base::Time& migration_time);
 
   void SetUniqueId(const std::string& guid);
 
diff --git a/sync/internal_api/public/engine/sync_status.cc b/sync/internal_api/public/engine/sync_status.cc
index 9ed3bf33b..4018a74 100644
--- a/sync/internal_api/public/engine/sync_status.cc
+++ b/sync/internal_api/public/engine/sync_status.cc
@@ -30,7 +30,9 @@
       useless_sync_cycles(0),
       useful_sync_cycles(0),
       cryptographer_ready(false),
-      crypto_has_pending_keys(false) {
+      crypto_has_pending_keys(false),
+      has_keystore_key(false),
+      passphrase_type(IMPLICIT_PASSPHRASE) {
 }
 
 SyncStatus::~SyncStatus() {
diff --git a/sync/internal_api/public/engine/sync_status.h b/sync/internal_api/public/engine/sync_status.h
index dfd9603..3b9229f 100644
--- a/sync/internal_api/public/engine/sync_status.h
+++ b/sync/internal_api/public/engine/sync_status.h
@@ -9,6 +9,7 @@
 
 #include "sync/base/sync_export.h"
 #include "sync/internal_api/public/base/model_type.h"
+#include "sync/internal_api/public/sync_encryption_handler.h"
 #include "sync/protocol/sync_protocol_error.h"
 
 namespace syncer {
@@ -84,6 +85,9 @@
   ModelTypeSet encrypted_types;
   bool cryptographer_ready;
   bool crypto_has_pending_keys;
+  bool has_keystore_key;
+  base::Time keystore_migration_time;
+  PassphraseType passphrase_type;
 
   // Per-datatype throttled status.
   ModelTypeSet throttled_types;
diff --git a/sync/internal_api/sync_encryption_handler_impl.cc b/sync/internal_api/sync_encryption_handler_impl.cc
index c8e26d2..2f15348 100644
--- a/sync/internal_api/sync_encryption_handler_impl.cc
+++ b/sync/internal_api/sync_encryption_handler_impl.cc
@@ -128,8 +128,7 @@
       encrypt_everything_(false),
       passphrase_type_(IMPLICIT_PASSPHRASE),
       keystore_key_(restored_keystore_key_for_bootstrapping),
-      nigori_overwrite_count_(0),
-      migration_time_ms_(0) {
+      nigori_overwrite_count_(0) {
   // We only bootstrap the user provided passphrase. The keystore key is handled
   // at Init time once we're sure the nigori is downloaded.
   vault_unsafe_.cryptographer.Bootstrap(restored_key_for_bootstrapping);
@@ -594,6 +593,10 @@
   return IsNigoriMigratedToKeystore(nigori_node.GetNigoriSpecifics());
 }
 
+base::Time SyncEncryptionHandlerImpl::migration_time() const {
+  return migration_time_;
+}
+
 // This function iterates over all encrypted types.  There are many scenarios in
 // which data for some or all types is not currently available.  In that case,
 // the lookup of the root node will fail and we will skip encryption for that
@@ -674,7 +677,8 @@
                                                                   trans);
   bool is_nigori_migrated = IsNigoriMigratedToKeystore(nigori);
   if (is_nigori_migrated) {
-    migration_time_ms_ = nigori.keystore_migration_time();
+    DCHECK(nigori.has_keystore_migration_time());
+    migration_time_ = ProtoTimeToTime(nigori.keystore_migration_time());
     PassphraseType nigori_passphrase_type =
         ProtoPassphraseTypeToEnum(nigori.passphrase_type());
 
@@ -1142,10 +1146,7 @@
     return false;
 
   DVLOG(1) << "Starting nigori migration to keystore support.";
-  if (migration_time_ms_ == 0)
-    migration_time_ms_ = TimeToProtoTime(base::Time::Now());
   sync_pb::NigoriSpecifics migrated_nigori(old_nigori);
-  migrated_nigori.set_keystore_migration_time(migration_time_ms_);
 
   PassphraseType new_passphrase_type = passphrase_type_;
   bool new_encrypt_everything = encrypt_everything_;
@@ -1189,13 +1190,23 @@
     return false;
   }
 
+  if (migration_time_.is_null())
+    migration_time_ = base::Time::Now();
+  migrated_nigori.set_keystore_migration_time(TimeToProtoTime(migration_time_));
+
   DVLOG(1) << "Completing nigori migration to keystore support.";
   nigori_node->SetNigoriSpecifics(migrated_nigori);
+
+  FOR_EACH_OBSERVER(
+      SyncEncryptionHandler::Observer,
+      observers_,
+      OnCryptographerStateChanged(cryptographer));
   if (passphrase_type_ != new_passphrase_type) {
     passphrase_type_ = new_passphrase_type;
     FOR_EACH_OBSERVER(SyncEncryptionHandler::Observer, observers_,
                       OnPassphraseTypeChanged(passphrase_type_));
   }
+
   if (new_encrypt_everything && !encrypt_everything_) {
     EnableEncryptEverythingImpl(trans->GetWrappedTrans());
     ReEncryptEverything(trans);
diff --git a/sync/internal_api/sync_encryption_handler_impl.h b/sync/internal_api/sync_encryption_handler_impl.h
index f54055b9..27f7080 100644
--- a/sync/internal_api/sync_encryption_handler_impl.h
+++ b/sync/internal_api/sync_encryption_handler_impl.h
@@ -9,6 +9,7 @@
 
 #include "base/compiler_specific.h"
 #include "base/gtest_prod_util.h"
+#include "base/time.h"
 #include "base/threading/thread_checker.h"
 #include "base/memory/scoped_ptr.h"
 #include "base/memory/weak_ptr.h"
@@ -62,9 +63,6 @@
   virtual bool EncryptEverythingEnabled() const OVERRIDE;
   virtual PassphraseType GetPassphraseType() const OVERRIDE;
 
-  // TODO(zea): provide a method for getting the time at which the nigori
-  // node was migrated.
-
   // NigoriHandler implementation.
   // Note: all methods are invoked while the caller holds a transaction.
   virtual void ApplyNigoriUpdate(
@@ -88,6 +86,7 @@
   ModelTypeSet GetEncryptedTypesUnsafe();
 
   bool MigratedToKeystore();
+  base::Time migration_time() const;
 
  private:
   FRIEND_TEST_ALL_PREFIXES(SyncEncryptionHandlerImplTest,
@@ -287,8 +286,8 @@
   // instantiation.
   int nigori_overwrite_count_;
 
-  // The time (in ms) the nigori was migrated to support keystore encryption.
-  int64 migration_time_ms_;
+  // The time the nigori was migrated to support keystore encryption.
+  base::Time migration_time_;
 
   DISALLOW_COPY_AND_ASSIGN(SyncEncryptionHandlerImpl);
 };
diff --git a/sync/internal_api/sync_encryption_handler_impl_unittest.cc b/sync/internal_api/sync_encryption_handler_impl_unittest.cc
index b07c1fe..aa9b9de 100644
--- a/sync/internal_api/sync_encryption_handler_impl_unittest.cc
+++ b/sync/internal_api/sync_encryption_handler_impl_unittest.cc
@@ -32,6 +32,7 @@
 namespace {
 
 using ::testing::_;
+using ::testing::AnyNumber;
 using ::testing::Mock;
 using ::testing::SaveArg;
 using ::testing::StrictMock;
@@ -384,7 +385,7 @@
       current_nigori_specifics.mutable_encryption_keybag());
   current_nigori_specifics.set_encrypt_everything(true);
 
-  EXPECT_CALL(*observer(), OnCryptographerStateChanged(_));
+  EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(), OnEncryptedTypesChanged(
       HasModelTypes(UserTypes()), true));
   {
@@ -401,7 +402,7 @@
   sync_pb::NigoriSpecifics old_nigori;
   other_cryptographer.GetKeys(old_nigori.mutable_encryption_keybag());
 
-  EXPECT_CALL(*observer(), OnCryptographerStateChanged(_));
+  EXPECT_CALL(*observer(), OnCryptographerStateChanged(_)).Times(AnyNumber());
   {
     // Update the encryption handler.
     WriteTransaction trans(FROM_HERE, user_share());
@@ -512,7 +513,7 @@
     nigori.set_keybag_is_frozen(false);
     nigori.set_encrypt_everything(false);
     EXPECT_CALL(*observer(),
-                OnCryptographerStateChanged(_));
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
     EXPECT_CALL(*observer(),
                 OnPassphraseRequired(_, _));
     encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans());
@@ -524,7 +525,7 @@
   Mock::VerifyAndClearExpectations(observer());
 
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnPassphraseAccepted());
   EXPECT_CALL(*observer(),
@@ -568,7 +569,7 @@
     nigori.set_keybag_is_frozen(true);
     nigori.set_encrypt_everything(false);
     EXPECT_CALL(*observer(),
-                OnCryptographerStateChanged(_));
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
     EXPECT_CALL(*observer(),
                 OnPassphraseRequired(_, _));
     EXPECT_CALL(*observer(),
@@ -582,7 +583,7 @@
   Mock::VerifyAndClearExpectations(observer());
 
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnPassphraseAccepted());
   EXPECT_CALL(*observer(),
@@ -606,7 +607,7 @@
   KeyParams current_key = {"localhost", "dummy", kCurKey};
   GetCryptographer()->AddKey(current_key);
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, false));
   EXPECT_CALL(*observer(),
@@ -618,6 +619,8 @@
     ReadTransaction trans(FROM_HERE, user_share());
     // Once we provide a keystore key, we should perform the migration.
     EXPECT_CALL(*observer(),
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
+    EXPECT_CALL(*observer(),
                 OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
     encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
                                          trans.GetWrappedTrans());
@@ -642,7 +645,7 @@
   KeyParams current_key = {"localhost", "dummy", kCurKey};
   GetCryptographer()->AddKey(current_key);
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, false));
   EXPECT_CALL(*observer(),
@@ -660,6 +663,8 @@
     ReadTransaction trans(FROM_HERE, user_share());
     // Once we provide a keystore key, we should perform the migration.
     EXPECT_CALL(*observer(),
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
+    EXPECT_CALL(*observer(),
                 OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
     encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
                                          trans.GetWrappedTrans());
@@ -682,7 +687,7 @@
        MigrateOnKeystoreKeyAvailableCustomWithEncryption) {
   const char kCurKey[] = "cur";
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_)).Times(2);
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnPassphraseRequired(_, _));
   EXPECT_CALL(*observer(),
@@ -710,6 +715,8 @@
     ReadTransaction trans(FROM_HERE, user_share());
     // Once we provide a keystore key, we should perform the migration.
     EXPECT_CALL(*observer(),
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
+    EXPECT_CALL(*observer(),
                 OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
     encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
                                          trans.GetWrappedTrans());
@@ -730,7 +737,7 @@
        MigrateOnKeystoreKeyAvailableCustomNoEncryption) {
   const char kCurKey[] = "cur";
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_)).Times(2);
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnPassphraseRequired(_, _));
   EXPECT_CALL(*observer(),
@@ -751,6 +758,8 @@
     ReadTransaction trans(FROM_HERE, user_share());
     // Once we provide a keystore key, we should perform the migration.
     EXPECT_CALL(*observer(),
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
+    EXPECT_CALL(*observer(),
                 OnBootstrapTokenUpdated(_, KEYSTORE_BOOTSTRAP_TOKEN));
     encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
                                          trans.GetWrappedTrans());
@@ -808,7 +817,7 @@
     EXPECT_CALL(*observer(),
                 OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE));
     EXPECT_CALL(*observer(),
-                OnCryptographerStateChanged(_)).Times(2);
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
     encryption_handler()->SetKeystoreKey(kRawKeystoreKey,
                                          trans.GetWrappedTrans());
     encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans());
@@ -864,7 +873,7 @@
     EXPECT_CALL(*observer(),
                 OnPassphraseRequired(_, _));
     EXPECT_CALL(*observer(),
-                OnCryptographerStateChanged(_));
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
     EXPECT_CALL(*observer(),
                 OnEncryptedTypesChanged(_, true));
     WriteTransaction trans(FROM_HERE, user_share());
@@ -893,7 +902,7 @@
   EXPECT_CALL(*observer(),
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptionComplete());
   EXPECT_CALL(*observer(),
@@ -943,7 +952,7 @@
     EXPECT_CALL(*observer(),
                 OnPassphraseRequired(_, _));
     EXPECT_CALL(*observer(),
-                OnCryptographerStateChanged(_));
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
     EXPECT_CALL(*observer(),
                 OnEncryptedTypesChanged(_, true));
     WriteTransaction trans(FROM_HERE, user_share());
@@ -970,7 +979,7 @@
   EXPECT_CALL(*observer(),
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptionComplete());
   EXPECT_CALL(*observer(),
@@ -1023,7 +1032,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseTypeChanged(CUSTOM_PASSPHRASE));
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, true)).Times(2);
   EXPECT_CALL(*observer(),
@@ -1047,7 +1056,7 @@
   // Now build an old unmigrated nigori node with old encrypted types. We should
   // properly overwrite it with the migrated + encrypt everything state.
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   {
     Cryptographer other_cryptographer(GetCryptographer()->encryptor());
     other_cryptographer.AddKey(old_key);
@@ -1101,7 +1110,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseTypeChanged(CUSTOM_PASSPHRASE));
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, true)).Times(2);
   EXPECT_CALL(*observer(),
@@ -1125,7 +1134,7 @@
   // Now build an old keystore nigori node with old encrypted types. We should
   // properly overwrite it with the migrated + encrypt everything state.
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   {
     WriteTransaction trans(FROM_HERE, user_share());
     WriteNode nigori_node(&trans);
@@ -1190,7 +1199,7 @@
     EXPECT_CALL(*observer(),
                 OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE));
     EXPECT_CALL(*observer(),
-                OnCryptographerStateChanged(_));
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
     EXPECT_CALL(*observer(),
                 OnPassphraseRequired(_, _));
     encryption_handler()->ApplyNigoriUpdate(nigori, trans.GetWrappedTrans());
@@ -1205,7 +1214,7 @@
   Mock::VerifyAndClearExpectations(observer());
 
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
   {
@@ -1275,7 +1284,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE));
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_)).Times(2);
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, false));
   EXPECT_CALL(*observer(),
@@ -1291,7 +1300,7 @@
 
   const char kNewKey[] = "new_key";
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnPassphraseTypeChanged(CUSTOM_PASSPHRASE));
   EXPECT_CALL(*observer(),
@@ -1372,7 +1381,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE));
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, false));
   encryption_handler()->Init();
@@ -1385,7 +1394,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseAccepted());
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
   EXPECT_CALL(*observer(),
@@ -1397,7 +1406,7 @@
 
   const char kNewKey[] = "new_key";
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnPassphraseTypeChanged(CUSTOM_PASSPHRASE));
   EXPECT_CALL(*observer(),
@@ -1478,7 +1487,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE));
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, false));
   encryption_handler()->Init();
@@ -1491,7 +1500,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseAccepted());
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
   EXPECT_CALL(*observer(),
@@ -1572,7 +1581,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseTypeChanged(KEYSTORE_PASSPHRASE));
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, false));
   encryption_handler()->Init();
@@ -1585,7 +1594,7 @@
   EXPECT_CALL(*observer(),
               OnPassphraseAccepted());
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnBootstrapTokenUpdated(_, PASSPHRASE_BOOTSTRAP_TOKEN));
   EXPECT_CALL(*observer(),
@@ -1599,6 +1608,8 @@
               OnEncryptionComplete());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, true));
+  EXPECT_CALL(*observer(),
+                OnCryptographerStateChanged(_)).Times(AnyNumber());
   encryption_handler()->EnableEncryptEverything();
   Mock::VerifyAndClearExpectations(observer());
 
@@ -1642,7 +1653,7 @@
   EXPECT_TRUE(other_cryptographer.is_ready());
 
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   EXPECT_CALL(*observer(),
               OnEncryptedTypesChanged(_, false));
   EXPECT_CALL(*observer(),
@@ -1668,7 +1679,7 @@
 
   // Now build an old keystore passphrase nigori node.
   EXPECT_CALL(*observer(),
-              OnCryptographerStateChanged(_));
+              OnCryptographerStateChanged(_)).Times(AnyNumber());
   {
     WriteTransaction trans(FROM_HERE, user_share());
     WriteNode nigori_node(&trans);
diff --git a/sync/internal_api/sync_manager_impl.cc b/sync/internal_api/sync_manager_impl.cc
index 5e0a0c3..20852f4 100644
--- a/sync/internal_api/sync_manager_impl.cc
+++ b/sync/internal_api/sync_manager_impl.cc
@@ -383,6 +383,8 @@
   unrecoverable_error_handler_ = unrecoverable_error_handler;
   report_unrecoverable_error_function_ = report_unrecoverable_error_function;
 
+  allstatus_.SetHasKeystoreKey(
+      !restored_keystore_key_for_bootstrapping.empty());
   sync_encryption_handler_.reset(new SyncEncryptionHandlerImpl(
       &share_,
       encryptor,
@@ -534,7 +536,8 @@
 void SyncManagerImpl::OnBootstrapTokenUpdated(
     const std::string& bootstrap_token,
     BootstrapTokenType type) {
-  // Does nothing.
+  if (type == KEYSTORE_BOOTSTRAP_TOKEN)
+    allstatus_.SetHasKeystoreKey(true);
 }
 
 void SyncManagerImpl::OnEncryptedTypesChanged(ModelTypeSet encrypted_types,
@@ -550,10 +553,14 @@
     Cryptographer* cryptographer) {
   allstatus_.SetCryptographerReady(cryptographer->is_ready());
   allstatus_.SetCryptoHasPendingKeys(cryptographer->has_pending_keys());
+  allstatus_.SetKeystoreMigrationTime(
+      sync_encryption_handler_->migration_time());
 }
 
 void SyncManagerImpl::OnPassphraseTypeChanged(PassphraseType type) {
-  // Does nothing.
+  allstatus_.SetPassphraseType(type);
+  allstatus_.SetKeystoreMigrationTime(
+      sync_encryption_handler_->migration_time());
 }
 
 void SyncManagerImpl::StartSyncingNormally(
diff --git a/sync/sync.gyp b/sync/sync.gyp
index 1d290988..cdbbd16 100644
--- a/sync/sync.gyp
+++ b/sync/sync.gyp
@@ -402,6 +402,7 @@
         'api/sync_error.cc',
         'api/sync_error_factory.h',
         'api/sync_error_factory.cc',
+        'api/time.h',
       ],
     },