Home | History | Annotate | Download | only in sync
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "chrome/browser/sync/about_sync_util.h"
      6 
      7 #include <string>
      8 
      9 #include "base/strings/string16.h"
     10 #include "base/values.h"
     11 #include "chrome/browser/signin/signin_manager.h"
     12 #include "chrome/browser/sync/profile_sync_service.h"
     13 #include "chrome/common/chrome_version_info.h"
     14 #include "sync/api/time.h"
     15 #include "sync/internal_api/public/util/sync_string_conversions.h"
     16 #include "sync/protocol/proto_enum_conversions.h"
     17 
     18 using base::DictionaryValue;
     19 using base::ListValue;
     20 
     21 const char kCredentialsTitle[] = "Credentials";
     22 const char kDetailsKey[] = "details";
     23 
     24 namespace {
     25 
     26 // Creates a 'section' for display on about:sync, consisting of a title and a
     27 // list of fields.  Returns a pointer to the new section.  Note that
     28 // |parent_list|, not the caller, owns the newly added section.
     29 ListValue* AddSection(ListValue* parent_list, const std::string& title) {
     30   DictionaryValue* section = new DictionaryValue();
     31   ListValue* section_contents = new ListValue();
     32   section->SetString("title", title);
     33   section->Set("data", section_contents);
     34   parent_list->Append(section);
     35   return section_contents;
     36 }
     37 
     38 // The following helper classes help manage the about:sync fields which will be
     39 // populated in method in ConstructAboutInformation.
     40 //
     41 // Each instance of one of thse classes indicates a field in about:sync.  Each
     42 // field will be serialized to a DictionaryValue with entries for 'stat_name',
     43 // 'stat_value' and 'is_valid'.
     44 
     45 class StringSyncStat {
     46  public:
     47   StringSyncStat(ListValue* section, const std::string& key);
     48   void SetValue(const std::string& value);
     49   void SetValue(const string16& value);
     50 
     51  private:
     52   // Owned by the |section| passed in during construction.
     53   DictionaryValue* stat_;
     54 };
     55 
     56 StringSyncStat::StringSyncStat(ListValue* section, const std::string& key) {
     57   stat_ = new DictionaryValue();
     58   stat_->SetString("stat_name", key);
     59   stat_->SetString("stat_value", "Uninitialized");
     60   stat_->SetBoolean("is_valid", false);
     61   section->Append(stat_);
     62 }
     63 
     64 void StringSyncStat::SetValue(const std::string& value) {
     65   stat_->SetString("stat_value", value);
     66   stat_->SetBoolean("is_valid", true);
     67 }
     68 
     69 void StringSyncStat::SetValue(const string16& value) {
     70   stat_->SetString("stat_value", value);
     71   stat_->SetBoolean("is_valid", true);
     72 }
     73 
     74 class BoolSyncStat {
     75  public:
     76   BoolSyncStat(ListValue* section, const std::string& key);
     77   void SetValue(bool value);
     78 
     79  private:
     80   // Owned by the |section| passed in during construction.
     81   DictionaryValue* stat_;
     82 };
     83 
     84 BoolSyncStat::BoolSyncStat(ListValue* section, const std::string& key) {
     85   stat_ = new DictionaryValue();
     86   stat_->SetString("stat_name", key);
     87   stat_->SetBoolean("stat_value", false);
     88   stat_->SetBoolean("is_valid", false);
     89   section->Append(stat_);
     90 }
     91 
     92 void BoolSyncStat::SetValue(bool value) {
     93   stat_->SetBoolean("stat_value", value);
     94   stat_->SetBoolean("is_valid", true);
     95 }
     96 
     97 class IntSyncStat {
     98  public:
     99   IntSyncStat(ListValue* section, const std::string& key);
    100   void SetValue(int value);
    101 
    102  private:
    103   // Owned by the |section| passed in during construction.
    104   DictionaryValue* stat_;
    105 };
    106 
    107 IntSyncStat::IntSyncStat(ListValue* section, const std::string& key) {
    108   stat_ = new DictionaryValue();
    109   stat_->SetString("stat_name", key);
    110   stat_->SetInteger("stat_value", 0);
    111   stat_->SetBoolean("is_valid", false);
    112   section->Append(stat_);
    113 }
    114 
    115 void IntSyncStat::SetValue(int value) {
    116   stat_->SetInteger("stat_value", value);
    117   stat_->SetBoolean("is_valid", true);
    118 }
    119 
    120 // Returns a string describing the chrome version environment. Version format:
    121 // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
    122 // If version information is unavailable, returns "invalid."
    123 // TODO(zea): this approximately matches MakeUserAgentForSyncApi in
    124 // sync_backend_host.cc. Unify the two if possible.
    125 std::string GetVersionString() {
    126   // Build a version string that matches MakeUserAgentForSyncApi with the
    127   // addition of channel info and proper OS names.
    128   chrome::VersionInfo chrome_version;
    129   if (!chrome_version.is_valid())
    130     return "invalid";
    131   // GetVersionStringModifier returns empty string for stable channel or
    132   // unofficial builds, the channel string otherwise. We want to have "-devel"
    133   // for unofficial builds only.
    134   std::string version_modifier =
    135       chrome::VersionInfo::GetVersionStringModifier();
    136   if (version_modifier.empty()) {
    137     if (chrome::VersionInfo::GetChannel() !=
    138             chrome::VersionInfo::CHANNEL_STABLE) {
    139       version_modifier = "-devel";
    140     }
    141   } else {
    142     version_modifier = " " + version_modifier;
    143   }
    144   return chrome_version.Name() + " " + chrome_version.OSType() + " " +
    145       chrome_version.Version() + " (" + chrome_version.LastChange() + ")" +
    146       version_modifier;
    147 }
    148 
    149 std::string GetTimeStr(base::Time time, const std::string& default_msg) {
    150   std::string time_str;
    151   if (time.is_null())
    152     time_str = default_msg;
    153   else
    154     time_str = syncer::GetTimeDebugString(time);
    155   return time_str;
    156 }
    157 
    158 }  // namespace
    159 
    160 namespace sync_ui_util {
    161 
    162 // This function both defines the structure of the message to be returned and
    163 // its contents.  Most of the message consists of simple fields in about:sync
    164 // which are grouped into sections and populated with the help of the SyncStat
    165 // classes defined above.
    166 scoped_ptr<DictionaryValue> ConstructAboutInformation(
    167     ProfileSyncService* service) {
    168   scoped_ptr<DictionaryValue> about_info(new DictionaryValue());
    169   ListValue* stats_list = new ListValue();  // 'details': A list of sections.
    170 
    171   // The following lines define the sections and their fields.  For each field,
    172   // a class is instantiated, which allows us to reference the fields in
    173   // 'setter' code later on in this function.
    174   ListValue* section_summary = AddSection(stats_list, "Summary");
    175   StringSyncStat summary_string(section_summary, "Summary");
    176 
    177   ListValue* section_version = AddSection(stats_list, "Version Info");
    178   StringSyncStat client_version(section_version, "Client Version");
    179   StringSyncStat server_url(section_version, "Server URL");
    180 
    181   ListValue* section_credentials = AddSection(stats_list, kCredentialsTitle);
    182   StringSyncStat sync_id(section_credentials, "Sync Client ID");
    183   StringSyncStat invalidator_id(section_credentials, "Invalidator Client ID");
    184   StringSyncStat username(section_credentials, "Username");
    185   BoolSyncStat is_token_available(section_credentials, "Sync Token Available");
    186 
    187   ListValue* section_local = AddSection(stats_list, "Local State");
    188   StringSyncStat last_synced(section_local, "Last Synced");
    189   BoolSyncStat is_setup_complete(section_local,
    190                                  "Sync First-Time Setup Complete");
    191   StringSyncStat backend_initialization(section_local,
    192                                         "Sync Backend Initialization");
    193   BoolSyncStat is_syncing(section_local, "Syncing");
    194 
    195   ListValue* section_network = AddSection(stats_list, "Network");
    196   BoolSyncStat is_throttled(section_network, "Throttled");
    197   StringSyncStat retry_time(section_network, "Retry time (maybe stale)");
    198   BoolSyncStat are_notifications_enabled(section_network,
    199                                          "Notifications Enabled");
    200 
    201   ListValue* section_encryption = AddSection(stats_list, "Encryption");
    202   BoolSyncStat is_using_explicit_passphrase(section_encryption,
    203                                             "Explicit Passphrase");
    204   BoolSyncStat is_passphrase_required(section_encryption,
    205                                       "Passphrase Required");
    206   BoolSyncStat is_cryptographer_ready(section_encryption,
    207                                       "Cryptographer Ready");
    208   BoolSyncStat has_pending_keys(section_encryption,
    209                                 "Cryptographer Has Pending Keys");
    210   StringSyncStat encrypted_types(section_encryption, "Encrypted Types");
    211   BoolSyncStat has_keystore_key(section_encryption, "Has Keystore Key");
    212   StringSyncStat keystore_migration_time(section_encryption,
    213                                          "Keystore Migration Time");
    214   StringSyncStat passphrase_type(section_encryption,
    215                                  "Passphrase Type");
    216   StringSyncStat passphrase_time(section_encryption,
    217                                  "Passphrase Time");
    218 
    219   ListValue* section_last_session = AddSection(
    220       stats_list, "Status from Last Completed Session");
    221   StringSyncStat session_source(section_last_session, "Sync Source");
    222   StringSyncStat get_key_result(section_last_session, "GetKey Step Result");
    223   StringSyncStat download_result(section_last_session, "Download Step Result");
    224   StringSyncStat commit_result(section_last_session, "Commit Step Result");
    225 
    226   ListValue* section_counters = AddSection(stats_list, "Running Totals");
    227   IntSyncStat notifications_received(section_counters,
    228                                      "Notifications Received");
    229   IntSyncStat empty_get_updates(section_counters, "Cycles Without Updates");
    230   IntSyncStat non_empty_get_updates(section_counters, "Cycles With Updated");
    231   IntSyncStat sync_cycles_without_commits(section_counters,
    232                                           "Cycles Without Commits");
    233   IntSyncStat sync_cycles_with_commits(section_counters, "Cycles With Commits");
    234   IntSyncStat useless_sync_cycles(section_counters,
    235                            "Cycles Without Commits or Updates");
    236   IntSyncStat useful_sync_cycles(section_counters,
    237                                  "Cycles With Commit or Update");
    238   IntSyncStat updates_received(section_counters, "Updates Downloaded");
    239   IntSyncStat tombstone_updates(section_counters, "Tombstone Updates");
    240   IntSyncStat reflected_updates(section_counters, "Reflected Updates");
    241   IntSyncStat successful_commits(section_counters, "Successful Commits");
    242   IntSyncStat conflicts_resolved_local_wins(section_counters,
    243                                      "Conflicts Resolved: Client Wins");
    244   IntSyncStat conflicts_resolved_server_wins(section_counters,
    245                                       "Conflicts Resolved: Server Wins");
    246 
    247   ListValue *section_this_cycle = AddSection(stats_list,
    248                                              "Transient Counters (this cycle)");
    249   IntSyncStat encryption_conflicts(section_this_cycle, "Encryption Conflicts");
    250   IntSyncStat hierarchy_conflicts(section_this_cycle, "Hierarchy Conflicts");
    251   IntSyncStat server_conflicts(section_this_cycle, "Server Conflicts");
    252   IntSyncStat committed_items(section_this_cycle, "Committed Items");
    253   IntSyncStat updates_remaining(section_this_cycle, "Updates Remaining");
    254 
    255   ListValue* section_that_cycle = AddSection(
    256       stats_list, "Transient Counters (last cycle of last completed session)");
    257   IntSyncStat updates_downloaded(section_that_cycle, "Updates Downloaded");
    258   IntSyncStat committed_count(section_that_cycle, "Committed Count");
    259   IntSyncStat entries(section_that_cycle, "Entries");
    260 
    261   ListValue* section_nudge_info = AddSection(
    262       stats_list, "Nudge Source Counters");
    263   IntSyncStat nudge_source_notification(
    264       section_nudge_info, "Server Invalidations");
    265   IntSyncStat nudge_source_local(section_nudge_info, "Local Changes");
    266   IntSyncStat nudge_source_local_refresh(section_nudge_info, "Local Refreshes");
    267 
    268   // This list of sections belongs in the 'details' field of the returned
    269   // message.
    270   about_info->Set(kDetailsKey, stats_list);
    271 
    272   // Populate all the fields we declared above.
    273   client_version.SetValue(GetVersionString());
    274 
    275   if (!service) {
    276     summary_string.SetValue("Sync service does not exist");
    277     return about_info.Pass();
    278   }
    279 
    280   syncer::SyncStatus full_status;
    281   bool is_status_valid = service->QueryDetailedSyncStatus(&full_status);
    282   bool sync_initialized = service->sync_initialized();
    283   const syncer::sessions::SyncSessionSnapshot& snapshot =
    284       sync_initialized ?
    285       service->GetLastSessionSnapshot() :
    286       syncer::sessions::SyncSessionSnapshot();
    287 
    288   if (is_status_valid)
    289     summary_string.SetValue(service->QuerySyncStatusSummary());
    290 
    291   server_url.SetValue(service->sync_service_url().spec());
    292 
    293   if (is_status_valid && !full_status.sync_id.empty())
    294     sync_id.SetValue(full_status.sync_id);
    295   if (is_status_valid && !full_status.invalidator_client_id.empty())
    296     invalidator_id.SetValue(full_status.invalidator_client_id);
    297   if (service->signin())
    298     username.SetValue(service->signin()->GetAuthenticatedUsername());
    299   is_token_available.SetValue(service->IsOAuthRefreshTokenAvailable());
    300 
    301   last_synced.SetValue(service->GetLastSyncedTimeString());
    302   is_setup_complete.SetValue(service->HasSyncSetupCompleted());
    303   backend_initialization.SetValue(
    304       service->GetBackendInitializationStateString());
    305   if (is_status_valid) {
    306     is_syncing.SetValue(full_status.syncing);
    307     retry_time.SetValue(GetTimeStr(full_status.retry_time,
    308         "Scheduler is not in backoff or throttled"));
    309   }
    310 
    311   if (snapshot.is_initialized())
    312     is_throttled.SetValue(snapshot.is_silenced());
    313   if (is_status_valid) {
    314     are_notifications_enabled.SetValue(
    315         full_status.notifications_enabled);
    316   }
    317 
    318   if (sync_initialized) {
    319     is_using_explicit_passphrase.SetValue(
    320         service->IsUsingSecondaryPassphrase());
    321     is_passphrase_required.SetValue(service->IsPassphraseRequired());
    322     passphrase_time.SetValue(
    323         GetTimeStr(service->GetExplicitPassphraseTime(), "No Passphrase Time"));
    324   }
    325   if (is_status_valid) {
    326     is_cryptographer_ready.SetValue(full_status.cryptographer_ready);
    327     has_pending_keys.SetValue(full_status.crypto_has_pending_keys);
    328     encrypted_types.SetValue(
    329         ModelTypeSetToString(full_status.encrypted_types));
    330     has_keystore_key.SetValue(full_status.has_keystore_key);
    331     keystore_migration_time.SetValue(
    332         GetTimeStr(full_status.keystore_migration_time, "Not Migrated"));
    333     passphrase_type.SetValue(
    334         PassphraseTypeToString(full_status.passphrase_type));
    335   }
    336 
    337   if (snapshot.is_initialized()) {
    338     if (snapshot.legacy_updates_source() !=
    339         sync_pb::GetUpdatesCallerInfo::UNKNOWN) {
    340       session_source.SetValue(
    341           syncer::GetUpdatesSourceString(snapshot.legacy_updates_source()));
    342     }
    343     get_key_result.SetValue(
    344         GetSyncerErrorString(
    345             snapshot.model_neutral_state().last_get_key_result));
    346     download_result.SetValue(
    347         GetSyncerErrorString(
    348             snapshot.model_neutral_state().last_download_updates_result));
    349     commit_result.SetValue(
    350         GetSyncerErrorString(
    351             snapshot.model_neutral_state().commit_result));
    352   }
    353 
    354   if (is_status_valid) {
    355     notifications_received.SetValue(full_status.notifications_received);
    356     empty_get_updates.SetValue(full_status.empty_get_updates);
    357     non_empty_get_updates.SetValue(full_status.nonempty_get_updates);
    358     sync_cycles_without_commits.SetValue(
    359         full_status.sync_cycles_without_commits);
    360     sync_cycles_with_commits.SetValue(
    361         full_status.sync_cycles_with_commits);
    362     useless_sync_cycles.SetValue(full_status.useless_sync_cycles);
    363     useful_sync_cycles.SetValue(full_status.useful_sync_cycles);
    364     updates_received.SetValue(full_status.updates_received);
    365     tombstone_updates.SetValue(full_status.tombstone_updates_received);
    366     reflected_updates.SetValue(full_status.reflected_updates_received);
    367     successful_commits.SetValue(full_status.num_commits_total);
    368     conflicts_resolved_local_wins.SetValue(
    369         full_status.num_local_overwrites_total);
    370     conflicts_resolved_server_wins.SetValue(
    371         full_status.num_server_overwrites_total);
    372   }
    373 
    374   if (is_status_valid) {
    375     encryption_conflicts.SetValue(full_status.encryption_conflicts);
    376     hierarchy_conflicts.SetValue(full_status.hierarchy_conflicts);
    377     server_conflicts.SetValue(full_status.server_conflicts);
    378     committed_items.SetValue(full_status.committed_count);
    379     updates_remaining.SetValue(full_status.updates_available);
    380   }
    381 
    382   if (is_status_valid) {
    383     nudge_source_notification.SetValue(full_status.nudge_source_notification);
    384     nudge_source_local.SetValue(full_status.nudge_source_local);
    385     nudge_source_local_refresh.SetValue(full_status.nudge_source_local_refresh);
    386   }
    387 
    388   if (snapshot.is_initialized()) {
    389     updates_downloaded.SetValue(
    390         snapshot.model_neutral_state().num_updates_downloaded_total);
    391     committed_count.SetValue(
    392         snapshot.model_neutral_state().num_successful_commits);
    393     entries.SetValue(snapshot.num_entries());
    394   }
    395 
    396   // The values set from this point onwards do not belong in the
    397   // details list.
    398 
    399   // We don't need to check is_status_valid here.
    400   // full_status.sync_protocol_error is exported directly from the
    401   // ProfileSyncService, even if the backend doesn't exist.
    402   const bool actionable_error_detected =
    403       full_status.sync_protocol_error.error_type != syncer::UNKNOWN_ERROR &&
    404       full_status.sync_protocol_error.error_type != syncer::SYNC_SUCCESS;
    405 
    406   about_info->SetBoolean("actionable_error_detected",
    407                          actionable_error_detected);
    408 
    409   // NOTE: We won't bother showing any of the following values unless
    410   // actionable_error_detected is set.
    411 
    412   ListValue* actionable_error = new ListValue();
    413   about_info->Set("actionable_error", actionable_error);
    414 
    415   StringSyncStat error_type(actionable_error, "Error Type");
    416   StringSyncStat action(actionable_error, "Action");
    417   StringSyncStat url(actionable_error, "URL");
    418   StringSyncStat description(actionable_error, "Error Description");
    419 
    420   if (actionable_error_detected) {
    421     error_type.SetValue(syncer::GetSyncErrorTypeString(
    422             full_status.sync_protocol_error.error_type));
    423     action.SetValue(syncer::GetClientActionString(
    424             full_status.sync_protocol_error.action));
    425     url.SetValue(full_status.sync_protocol_error.url);
    426     description.SetValue(full_status.sync_protocol_error.error_description);
    427   }
    428 
    429   about_info->SetBoolean("unrecoverable_error_detected",
    430                          service->HasUnrecoverableError());
    431 
    432   if (service->HasUnrecoverableError()) {
    433     tracked_objects::Location loc(service->unrecoverable_error_location());
    434     std::string location_str;
    435     loc.Write(true, true, &location_str);
    436     std::string unrecoverable_error_message =
    437         "Unrecoverable error detected at " + location_str +
    438         ": " + service->unrecoverable_error_message();
    439     about_info->SetString("unrecoverable_error_message",
    440                        unrecoverable_error_message);
    441   }
    442 
    443   about_info->Set("type_status", service->GetTypeStatusMap());
    444 
    445   return about_info.Pass();
    446 }
    447 
    448 }  // namespace sync_ui_util
    449