| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/sync/service/sync_internals_util.h" |
| |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/i18n/time_formatting.h" |
| #include "base/notreached.h" |
| #include "base/strings/strcat.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "build/build_config.h" |
| #include "components/sync/base/time.h" |
| #include "components/sync/engine/cycle/sync_cycle_snapshot.h" |
| #include "components/sync/engine/sync_status.h" |
| #include "components/sync/engine/sync_string_conversions.h" |
| #include "components/sync/protocol/proto_enum_conversions.h" |
| #include "components/sync/service/sync_service.h" |
| #include "components/sync/service/sync_token_status.h" |
| #include "components/sync/service/sync_user_settings.h" |
| #include "components/sync/service/trusted_vault_synthetic_field_trial.h" |
| #include "components/version_info/version_info.h" |
| #include "url/gurl.h" |
| |
| namespace syncer::sync_ui_util { |
| |
| namespace { |
| |
| constexpr char kUninitialized[] = "Uninitialized"; |
| |
| constexpr char kUninitializedCSSClass[] = "uninitialized"; |
| constexpr char kBadStateCSSClass[] = "in_bad_state"; |
| |
| std::string SeverityToString(TypeStatusForDebugging::Severity severity) { |
| switch (severity) { |
| case TypeStatusForDebugging::Severity::kError: |
| return "severity_error"; |
| case TypeStatusForDebugging::Severity::kWarning: |
| return "severity_warning"; |
| case TypeStatusForDebugging::Severity::kInfo: |
| return "severity_info"; |
| case TypeStatusForDebugging::Severity::kTransitioning: |
| return "transitioning"; |
| case TypeStatusForDebugging::Severity::kOk: |
| return "ok"; |
| } |
| NOTREACHED(); |
| } |
| |
| // Converts TypeStatusMapForDebugging to a base::Value::List. |
| base::Value::List TypeStatusMapToValueList( |
| const TypeStatusMapForDebugging& map) { |
| base::Value::List result; |
| auto type_status_header = base::Value::Dict() |
| .Set("status", "header") |
| .Set("name", "Data Type") |
| .Set("num_entries", "Total Entries") |
| .Set("num_live", "Live Entries") |
| .Set("message", "Message") |
| .Set("state", "State"); |
| result.Append(std::move(type_status_header)); |
| for (const auto& [type, status] : map) { |
| base::Value::Dict type_status; |
| type_status.Set("name", DataTypeToDebugString(type)); |
| type_status.Set("status", SeverityToString(status.severity)); |
| type_status.Set("state", status.state); |
| type_status.Set("message", status.message); |
| result.Append(std::move(type_status)); |
| } |
| return result; |
| } |
| |
| // This class represents one field in chrome://sync-internals. It gets |
| // serialized into a dictionary with entries for 'stat_name', 'stat_value' and |
| // 'stat_status'. |
| class StatBase { |
| public: |
| base::Value::Dict ToValue() const { |
| return base::Value::Dict() |
| .Set("stat_name", base::Value(key_)) |
| .Set("stat_value", value_.Clone()) |
| .Set("stat_status", base::Value(status_)); |
| } |
| |
| protected: |
| StatBase(const std::string& key, base::Value default_value) |
| : key_(key), value_(std::move(default_value)) {} |
| |
| void SetFromValue(base::Value value, bool is_good) { |
| value_ = std::move(value); |
| status_ = is_good ? "" : kBadStateCSSClass; |
| } |
| |
| private: |
| std::string key_; |
| base::Value value_; |
| std::string status_ = kUninitializedCSSClass; |
| }; |
| |
| template <typename T> |
| class Stat : public StatBase { |
| public: |
| Stat(const std::string& key, const T& default_value) |
| : StatBase(key, base::Value(default_value)) {} |
| |
| void Set(const T& value, bool is_good = true) { |
| SetFromValue(base::Value(value), is_good); |
| } |
| }; |
| |
| // A section for display on chrome://sync-internals, consisting of a title and a |
| // list of fields. |
| class Section { |
| public: |
| Section(const std::string& title, bool is_sensitive) |
| : title_(title), is_sensitive_(is_sensitive) {} |
| |
| Stat<bool>* AddBoolStat(const std::string& key) { |
| return AddStat(key, false); |
| } |
| Stat<int>* AddIntStat(const std::string& key) { return AddStat(key, 0); } |
| Stat<std::string>* AddStringStat(const std::string& key) { |
| return AddStat(key, std::string(kUninitialized)); |
| } |
| |
| base::Value::Dict ToValue() const { |
| base::Value::List stats; |
| for (const std::unique_ptr<StatBase>& stat : stats_) { |
| stats.Append(stat->ToValue()); |
| } |
| return base::Value::Dict() |
| .Set("title", base::Value(title_)) |
| .Set("data", std::move(stats)) |
| .Set("is_sensitive", base::Value(is_sensitive_)); |
| } |
| |
| bool is_sensitive() { return is_sensitive_; } |
| |
| private: |
| template <typename T> |
| Stat<T>* AddStat(const std::string& key, const T& default_value) { |
| auto stat = std::make_unique<Stat<T>>(key, default_value); |
| Stat<T>* result = stat.get(); |
| stats_.push_back(std::move(stat)); |
| return result; |
| } |
| |
| std::string title_; |
| std::vector<std::unique_ptr<StatBase>> stats_; |
| bool is_sensitive_ = false; |
| }; |
| |
| class SectionList { |
| public: |
| SectionList() = default; |
| |
| // WARNING: If this section includes any Personally Identifiable Information, |
| // `is_sensitive` should be set to true. |
| Section* AddSection(const std::string& title, bool is_sensitive) { |
| sections_.push_back(std::make_unique<Section>(title, is_sensitive)); |
| return sections_.back().get(); |
| } |
| |
| // If `include_sensitive_data` is true, returns all added sections. Otherwise, |
| // omits those added with `is_sensitive` set to true. |
| base::Value::List ToValue(IncludeSensitiveData include_sensitive_data) const { |
| base::Value::List result; |
| for (const std::unique_ptr<Section>& section : sections_) { |
| if (include_sensitive_data || !section->is_sensitive()) { |
| result.Append(section->ToValue()); |
| } |
| } |
| return result; |
| } |
| |
| private: |
| std::vector<std::unique_ptr<Section>> sections_; |
| }; |
| |
| std::string GetUserActionableErrorString( |
| SyncService::UserActionableError state) { |
| switch (state) { |
| case SyncService::UserActionableError::kNone: |
| return "None"; |
| case SyncService::UserActionableError::kSignInNeedsUpdate: |
| return "Sign-in needs update"; |
| case SyncService::UserActionableError::kNeedsPassphrase: |
| return "Needs passphrase"; |
| case SyncService::UserActionableError::kNeedsTrustedVaultKeyForPasswords: |
| return "Needs trusted vault key for passwords"; |
| case SyncService::UserActionableError::kNeedsTrustedVaultKeyForEverything: |
| return "Needs trusted vault key for everything"; |
| case SyncService::UserActionableError:: |
| kTrustedVaultRecoverabilityDegradedForPasswords: |
| return "Trusted vault recoverability degraded for passwords"; |
| case SyncService::UserActionableError:: |
| kTrustedVaultRecoverabilityDegradedForEverything: |
| return "Trusted vault recoverability degraded for everything"; |
| #if !BUILDFLAG(IS_IOS) |
| case SyncService::UserActionableError::kNeedsSettingsConfirmation: |
| return "Needs settings confirmation"; |
| case SyncService::UserActionableError::kUnrecoverableError: |
| return "Unrecoverable error"; |
| #endif // !BUILDFLAG(IS_IOS) |
| #if BUILDFLAG(IS_ANDROID) |
| case SyncService::UserActionableError::kNeedsUPMBackendUpgrade: |
| return "Needs UPM backend upgrade"; |
| #endif // BUILDFLAG(IS_ANDROID) |
| case SyncService::UserActionableError::kNeedsClientUpgrade: |
| return "Client version is too old and needs upgrade"; |
| } |
| |
| NOTREACHED(); |
| } |
| |
| // Returns a string describing the chrome version environment. Version format: |
| // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel"> |
| // If version information is unavailable, returns "invalid." |
| // TODO(zea): this approximately matches syncer::MakeUserAgentForSync in |
| // sync_util.h. Unify the two if possible. |
| std::string GetVersionString(const std::string& channel) { |
| // Build a version string that matches syncer::MakeUserAgentForSync with the |
| // addition of channel info and proper OS names. |
| // `channel` will be an empty string for stable channel or unofficial builds, |
| // the channel string otherwise. We want to have "-devel" for unofficial |
| // builds only. |
| std::string version_modifier = channel; |
| if (version_modifier.empty()) { |
| if (!version_info::IsOfficialBuild()) { |
| version_modifier = "-devel"; |
| } |
| } else { |
| version_modifier = " " + version_modifier; |
| } |
| return base::StrCat({version_info::GetProductName(), " ", |
| version_info::GetOSType(), " ", |
| version_info::GetVersionNumber(), " (", |
| version_info::GetLastChange(), ")", version_modifier}); |
| } |
| |
| std::string GetTimeStr(base::Time time, |
| const std::string& default_msg = "n/a") { |
| if (time.is_null()) { |
| return default_msg; |
| } |
| return GetTimeDebugString(time); |
| } |
| |
| std::string GetTimeStrFromProto(int64_t proto_time, |
| const std::string& default_msg = "n/a") { |
| if (proto_time == 0) { |
| return default_msg; |
| } |
| return GetTimeDebugString(ProtoTimeToTime(proto_time)); |
| } |
| |
| // Analogous to GetTimeDebugString from components/sync/base/time.h. Consider |
| // moving it there if more places need this. |
| std::string GetTimeDeltaDebugString(base::TimeDelta t) { |
| std::u16string result; |
| if (!base::TimeDurationFormat(t, base::DURATION_WIDTH_NUMERIC, &result)) { |
| return "Invalid TimeDelta?!"; |
| } |
| return base::UTF16ToUTF8(result); |
| } |
| |
| std::string GetLastSyncedTimeString(base::Time last_synced_time) { |
| if (last_synced_time.is_null()) { |
| return "Never"; |
| } |
| |
| base::TimeDelta time_since_last_sync = base::Time::Now() - last_synced_time; |
| |
| if (time_since_last_sync < base::Minutes(1)) { |
| return "Just now"; |
| } |
| |
| return GetTimeDeltaDebugString(time_since_last_sync) + " ago"; |
| } |
| |
| std::string GetConnectionStatus(const SyncTokenStatus& status) { |
| switch (status.connection_status) { |
| case CONNECTION_NOT_ATTEMPTED: |
| return "not attempted"; |
| case CONNECTION_OK: |
| return base::StringPrintf( |
| "OK since %s", |
| GetTimeStr(status.connection_status_update_time).c_str()); |
| case CONNECTION_AUTH_ERROR: |
| return base::StringPrintf( |
| "auth error since %s", |
| GetTimeStr(status.connection_status_update_time).c_str()); |
| case CONNECTION_SERVER_ERROR: |
| return base::StringPrintf( |
| "server error since %s", |
| GetTimeStr(status.connection_status_update_time).c_str()); |
| } |
| NOTREACHED(); |
| } |
| |
| } // namespace |
| |
| std::string GetDisableReasonsDebugString( |
| SyncService::DisableReasonSet disable_reasons) { |
| if (disable_reasons.empty()) { |
| return "None"; |
| } |
| std::vector<std::string> reason_strings; |
| if (disable_reasons.Has(SyncService::DISABLE_REASON_ENTERPRISE_POLICY)) { |
| reason_strings.push_back("Enterprise policy"); |
| } |
| if (disable_reasons.Has(SyncService::DISABLE_REASON_NOT_SIGNED_IN)) { |
| reason_strings.push_back("Not signed in"); |
| } |
| if (disable_reasons.Has(SyncService::DISABLE_REASON_UNRECOVERABLE_ERROR)) { |
| reason_strings.push_back("Unrecoverable error"); |
| } |
| return base::JoinString(reason_strings, ", "); |
| } |
| |
| std::string TransportStateStringToDebugString( |
| SyncService::TransportState state) { |
| switch (state) { |
| case SyncService::TransportState::DISABLED: |
| return "Disabled"; |
| case SyncService::TransportState::PAUSED: |
| return "Paused"; |
| case SyncService::TransportState::START_DEFERRED: |
| return "Start deferred"; |
| case SyncService::TransportState::INITIALIZING: |
| return "Initializing"; |
| case SyncService::TransportState::PENDING_DESIRED_CONFIGURATION: |
| return "Pending desired configuration"; |
| case SyncService::TransportState::CONFIGURING: |
| return "Configuring data types"; |
| case SyncService::TransportState::ACTIVE: |
| return "Active"; |
| } |
| NOTREACHED(); |
| } |
| |
| // This function both defines the structure of the message to be returned and |
| // its contents. Most of the message consists of simple fields in |
| // chrome://sync-internals which are grouped into sections and populated with |
| // the help of the SyncStat classes defined above. |
| base::Value::Dict ConstructAboutInformation( |
| IncludeSensitiveData include_sensitive_data, |
| SyncService* service, |
| const std::string& channel) { |
| base::Value::Dict about_info; |
| |
| SectionList section_list; |
| |
| Section* section_summary = |
| section_list.AddSection("Summary", /*is_sensitive=*/false); |
| Stat<std::string>* transport_state = |
| section_summary->AddStringStat("Transport State"); |
| Stat<std::string>* error_state = |
| section_summary->AddStringStat("User Actionable Error"); |
| Stat<std::string>* disable_reasons = |
| section_summary->AddStringStat("Disable Reasons"); |
| Stat<bool>* feature_enabled = |
| section_summary->AddBoolStat("Sync Feature Enabled"); |
| Stat<bool>* setup_in_progress = |
| section_summary->AddBoolStat("Setup In Progress"); |
| Stat<std::string>* auth_error = section_summary->AddStringStat("Auth Error"); |
| |
| Section* section_version = |
| section_list.AddSection("Version Info", /*is_sensitive=*/false); |
| Stat<std::string>* client_version = |
| section_version->AddStringStat("Client Version"); |
| Stat<std::string>* server_url = section_version->AddStringStat("Server URL"); |
| |
| Section* section_identity = |
| section_list.AddSection(kIdentityTitle, /*is_sensitive=*/true); |
| Stat<std::string>* sync_client_id = |
| section_identity->AddStringStat("Sync Client ID"); |
| Stat<std::string>* username = section_identity->AddStringStat("Username"); |
| Stat<bool>* user_has_consent = section_identity->AddBoolStat("Sync Consent"); |
| |
| Section* section_credentials = |
| section_list.AddSection("Credentials", /*is_sensitive=*/false); |
| Stat<std::string>* token_request_time = |
| section_credentials->AddStringStat("Requested Token"); |
| Stat<std::string>* token_response_time = |
| section_credentials->AddStringStat("Received Token Response"); |
| Stat<std::string>* last_token_request_result = |
| section_credentials->AddStringStat("Last Token Request Result"); |
| Stat<bool>* has_token = section_credentials->AddBoolStat("Has Token"); |
| Stat<std::string>* next_token_request = |
| section_credentials->AddStringStat("Next Token Request"); |
| |
| Section* section_local = |
| section_list.AddSection("Local State", /*is_sensitive=*/false); |
| Stat<std::string>* server_connection = |
| section_local->AddStringStat("Server Connection"); |
| Stat<std::string>* last_synced = section_local->AddStringStat("Last Synced"); |
| Stat<bool>* is_setup_complete = |
| section_local->AddBoolStat("Sync First-Time Setup Complete"); |
| Stat<bool>* is_syncing = section_local->AddBoolStat("Sync Cycle Ongoing"); |
| Stat<bool>* is_local_sync_enabled = |
| section_local->AddBoolStat("Local Sync Backend Enabled"); |
| Stat<std::string>* local_backend_path = |
| section_local->AddStringStat("Local Backend Path"); |
| |
| Section* section_network = |
| section_list.AddSection("Network", /*is_sensitive=*/false); |
| Stat<bool>* is_any_throttled_or_backoff = |
| section_network->AddBoolStat("Throttled or Backoff"); |
| Stat<std::string>* retry_time = section_network->AddStringStat("Retry Time"); |
| Stat<bool>* are_notifications_enabled = |
| section_network->AddBoolStat("Notifications Enabled"); |
| |
| Section* section_encryption = |
| section_list.AddSection("Encryption", /*is_sensitive=*/false); |
| Stat<bool>* is_using_explicit_passphrase = |
| section_encryption->AddBoolStat("Explicit Passphrase"); |
| Stat<bool>* is_passphrase_required = |
| section_encryption->AddBoolStat("Passphrase Required"); |
| Stat<bool>* cryptographer_can_encrypt = |
| section_encryption->AddBoolStat("Cryptographer Ready To Encrypt"); |
| Stat<bool>* has_pending_keys = |
| section_encryption->AddBoolStat("Cryptographer Has Pending Keys"); |
| Stat<std::string>* encrypted_types = |
| section_encryption->AddStringStat("Encrypted Types"); |
| Stat<bool>* has_keystore_key = |
| section_encryption->AddBoolStat("Has Keystore Key"); |
| Stat<std::string>* keystore_migration_time = |
| section_encryption->AddStringStat("Keystore Migration Time"); |
| Stat<std::string>* passphrase_type = |
| section_encryption->AddStringStat("Passphrase Type"); |
| Stat<std::string>* explicit_passphrase_time = |
| section_encryption->AddStringStat("Explicit passphrase Time"); |
| Stat<std::string>* trusted_vault_migration_time = |
| section_encryption->AddStringStat("Trusted Vault Migration Time"); |
| Stat<int>* trusted_vault_key_version = |
| section_encryption->AddIntStat("Trusted Vault Version/Epoch"); |
| Stat<std::string>* trusted_vault_auto_upgrade_experiment_group = |
| section_encryption->AddStringStat("Trusted Vault Auto Upgrade Group"); |
| |
| Section* section_last_session = section_list.AddSection( |
| "Status from Last Completed Session", /*is_sensitive=*/false); |
| Stat<std::string>* session_source = |
| section_last_session->AddStringStat("Sync Source"); |
| Stat<bool>* get_key_failed = |
| section_last_session->AddBoolStat("GetKey Step Failed"); |
| Stat<std::string>* download_result = |
| section_last_session->AddStringStat("Download Step Result"); |
| Stat<std::string>* commit_result = |
| section_last_session->AddStringStat("Commit Step Result"); |
| |
| Section* section_counters = |
| section_list.AddSection("Running Totals", /*is_sensitive=*/false); |
| Stat<int>* notifications_received = |
| section_counters->AddIntStat("Notifications Received"); |
| Stat<int>* updates_received = |
| section_counters->AddIntStat("Updates Downloaded"); |
| Stat<int>* tombstone_updates = |
| section_counters->AddIntStat("Tombstone Updates"); |
| Stat<int>* successful_commits = |
| section_counters->AddIntStat("Successful Commits"); |
| |
| Section* section_this_cycle = section_list.AddSection( |
| "Transient Counters (this cycle)", /*is_sensitive=*/false); |
| Stat<int>* server_conflicts = |
| section_this_cycle->AddIntStat("Server Conflicts"); |
| Stat<int>* committed_items = |
| section_this_cycle->AddIntStat("Committed Items"); |
| |
| Section* section_that_cycle = section_list.AddSection( |
| "Transient Counters (last cycle of last completed session)", |
| /*is_sensitive=*/false); |
| Stat<int>* updates_downloaded = |
| section_that_cycle->AddIntStat("Updates Downloaded"); |
| Stat<int>* committed_count = |
| section_that_cycle->AddIntStat("Committed Count"); |
| |
| // Populate all the fields we declared above. |
| client_version->Set(GetVersionString(channel)); |
| |
| if (!service) { |
| transport_state->Set("Sync service does not exist"); |
| error_state->Set("Sync service does not exist"); |
| about_info.Set(kDetailsKey, section_list.ToValue(include_sensitive_data)); |
| return about_info; |
| } |
| |
| // Summary. |
| transport_state->Set( |
| TransportStateStringToDebugString(service->GetTransportState())); |
| const SyncService::UserActionableError user_actionable_error = |
| service->GetUserActionableError(); |
| error_state->Set(GetUserActionableErrorString(user_actionable_error), |
| /*is_good=*/user_actionable_error == |
| SyncService::UserActionableError::kNone); |
| disable_reasons->Set( |
| GetDisableReasonsDebugString(service->GetDisableReasons())); |
| // TODO(crbug.com/40067058): Delete this when ConsentLevel::kSync is deleted. |
| // See ConsentLevel::kSync documentation for details. |
| feature_enabled->Set(service->IsSyncFeatureEnabled()); |
| setup_in_progress->Set(service->IsSetupInProgress()); |
| std::string auth_error_str = service->GetAuthError().ToString(); |
| auth_error->Set( |
| base::StringPrintf( |
| "%s since %s", |
| (auth_error_str.empty() ? "OK" : auth_error_str).c_str(), |
| GetTimeStr(service->GetAuthErrorTime(), "browser startup").c_str()), |
| /*is_good=*/auth_error_str.empty()); |
| |
| SyncStatus full_status; |
| bool is_status_valid = |
| service->QueryDetailedSyncStatusForDebugging(&full_status); |
| const SyncCycleSnapshot& snapshot = |
| service->GetLastCycleSnapshotForDebugging(); |
| const SyncTokenStatus& token_status = |
| service->GetSyncTokenStatusForDebugging(); |
| bool is_local_sync_enabled_state = service->IsLocalSyncEnabled(); |
| |
| // Version Info. |
| // `client_version` was already set above. |
| if (!is_local_sync_enabled_state) { |
| server_url->Set(service->GetSyncServiceUrlForDebugging().spec()); |
| } |
| |
| // Identity. |
| if (is_status_valid && !full_status.cache_guid.empty()) { |
| sync_client_id->Set(full_status.cache_guid); |
| } |
| if (!is_local_sync_enabled_state) { |
| username->Set(service->GetAccountInfo().email); |
| // TODO(crbug.com/40067058): Delete this when ConsentLevel::kSync is |
| // deleted. See ConsentLevel::kSync documentation for details. |
| user_has_consent->Set(service->HasSyncConsent()); |
| } |
| |
| // Credentials. |
| token_request_time->Set(GetTimeStr(token_status.token_request_time)); |
| token_response_time->Set(GetTimeStr(token_status.token_response_time)); |
| std::string err = token_status.last_get_token_error.error_message(); |
| last_token_request_result->Set(err.empty() ? "OK" : err, |
| /*is_good=*/err.empty()); |
| has_token->Set(token_status.has_token); |
| next_token_request->Set( |
| GetTimeStr(token_status.next_token_request_time, "not scheduled")); |
| |
| // Local State. |
| server_connection->Set( |
| GetConnectionStatus(token_status), |
| /*is_good=*/token_status.connection_status == CONNECTION_NOT_ATTEMPTED || |
| token_status.connection_status == CONNECTION_OK); |
| last_synced->Set( |
| GetLastSyncedTimeString(service->GetLastSyncedTimeForDebugging())); |
| // TODO(crbug.com/40067058): Delete this when ConsentLevel::kSync is deleted. |
| // See ConsentLevel::kSync documentation for details. |
| is_setup_complete->Set( |
| service->GetUserSettings()->IsInitialSyncFeatureSetupComplete()); |
| if (is_status_valid) { |
| is_syncing->Set(full_status.syncing); |
| } |
| is_local_sync_enabled->Set(is_local_sync_enabled_state); |
| if (is_local_sync_enabled_state && is_status_valid) { |
| local_backend_path->Set(full_status.local_sync_folder); |
| } |
| |
| // Network. |
| if (snapshot.is_initialized()) { |
| is_any_throttled_or_backoff->Set(snapshot.is_silenced()); |
| } |
| if (is_status_valid) { |
| retry_time->Set(GetTimeStr(full_status.retry_time, |
| "Scheduler is not in backoff or throttled")); |
| } |
| if (is_status_valid) { |
| are_notifications_enabled->Set( |
| full_status.notifications_enabled, |
| /*is_good=*/full_status.notifications_enabled); |
| } |
| |
| // Encryption. |
| if (service->IsEngineInitialized()) { |
| is_using_explicit_passphrase->Set( |
| service->GetUserSettings()->IsUsingExplicitPassphrase()); |
| is_passphrase_required->Set( |
| service->GetUserSettings()->IsPassphraseRequired()); |
| explicit_passphrase_time->Set( |
| GetTimeStr(service->GetUserSettings()->GetExplicitPassphraseTime())); |
| } |
| if (is_status_valid) { |
| cryptographer_can_encrypt->Set(full_status.cryptographer_can_encrypt); |
| has_pending_keys->Set(full_status.crypto_has_pending_keys); |
| encrypted_types->Set(DataTypeSetToDebugString(full_status.encrypted_types)); |
| has_keystore_key->Set(full_status.has_keystore_key); |
| keystore_migration_time->Set( |
| GetTimeStr(full_status.keystore_migration_time, "Not Migrated")); |
| passphrase_type->Set(PassphraseTypeToString(full_status.passphrase_type)); |
| |
| if (full_status.passphrase_type == |
| PassphraseType::kTrustedVaultPassphrase) { |
| trusted_vault_migration_time->Set(GetTimeStrFromProto( |
| full_status.trusted_vault_debug_info.migration_time())); |
| trusted_vault_key_version->Set( |
| full_status.trusted_vault_debug_info.key_version()); |
| } |
| |
| if (full_status.trusted_vault_debug_info |
| .has_auto_upgrade_experiment_group()) { |
| const TrustedVaultAutoUpgradeSyntheticFieldTrialGroup group = |
| TrustedVaultAutoUpgradeSyntheticFieldTrialGroup::FromProto( |
| full_status.trusted_vault_debug_info |
| .auto_upgrade_experiment_group()); |
| trusted_vault_auto_upgrade_experiment_group->Set( |
| group.is_valid() ? group.name() : std::string("Invalid")); |
| } |
| } |
| |
| // Status from Last Completed Session. |
| if (snapshot.is_initialized()) { |
| if (snapshot.get_updates_origin() != sync_pb::SyncEnums::UNKNOWN_ORIGIN) { |
| session_source->Set(ProtoEnumToString(snapshot.get_updates_origin())); |
| } |
| const bool get_key_failed_state = |
| snapshot.model_neutral_state().last_get_key_failed; |
| get_key_failed->Set(get_key_failed_state, |
| /*is_good=*/!get_key_failed_state); |
| SyncerError download_result_err = |
| snapshot.model_neutral_state().last_download_updates_result; |
| download_result->Set( |
| download_result_err.ToString(), |
| /*is_good=*/download_result_err.type() == SyncerError::Type::kSuccess); |
| SyncerError commit_result_err = |
| snapshot.model_neutral_state().commit_result; |
| commit_result->Set( |
| commit_result_err.ToString(), |
| /*is_good=*/commit_result_err.type() == SyncerError::Type::kSuccess); |
| } |
| |
| // Running Totals. |
| if (is_status_valid) { |
| notifications_received->Set(full_status.notifications_received); |
| updates_received->Set(full_status.updates_received); |
| tombstone_updates->Set(full_status.tombstone_updates_received); |
| successful_commits->Set(full_status.num_commits_total); |
| } |
| |
| // Transient Counters (this cycle). |
| if (is_status_valid) { |
| server_conflicts->Set(full_status.server_conflicts); |
| committed_items->Set(full_status.committed_count); |
| } |
| |
| // Transient Counters (last cycle of last completed session). |
| if (snapshot.is_initialized()) { |
| updates_downloaded->Set( |
| snapshot.model_neutral_state().num_updates_downloaded_total); |
| committed_count->Set(snapshot.model_neutral_state().num_successful_commits); |
| } |
| |
| // This list of sections belongs in the 'details' field of the returned |
| // message. |
| about_info.Set(kDetailsKey, section_list.ToValue(include_sensitive_data)); |
| |
| // The values set from this point onwards do not belong in the |
| // details list. |
| |
| // We don't need to check is_status_valid here. |
| // full_status.sync_protocol_error is exported directly from the |
| // SyncServiceImpl, even if the backend doesn't exist. |
| const bool actionable_error_detected = |
| full_status.sync_protocol_error.error_type != UNKNOWN_ERROR && |
| full_status.sync_protocol_error.error_type != SYNC_SUCCESS; |
| |
| about_info.Set("actionable_error_detected", |
| base::Value(actionable_error_detected)); |
| |
| // NOTE: We won't bother showing any of the following values unless |
| // actionable_error_detected is set. |
| |
| Stat<std::string> error_type("Error Type", kUninitialized); |
| Stat<std::string> action("Action", kUninitialized); |
| Stat<std::string> description("Error Description", kUninitialized); |
| |
| if (actionable_error_detected) { |
| error_type.Set( |
| GetSyncErrorTypeString(full_status.sync_protocol_error.error_type)); |
| action.Set(GetClientActionString(full_status.sync_protocol_error.action)); |
| description.Set(full_status.sync_protocol_error.error_description); |
| } |
| |
| about_info.Set("actionable_error", base::Value::List() |
| .Append(error_type.ToValue()) |
| .Append(action.ToValue()) |
| .Append(description.ToValue())); |
| |
| about_info.Set("unrecoverable_error_detected", |
| base::Value(service->HasUnrecoverableError())); |
| |
| // Sync-the-feature should not be enabled on mobile platforms, where the |
| // sync-to-signin migration is completed. |
| const bool allow_enabling_sync_the_feature = |
| !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_IOS); |
| |
| about_info.Set("allow_enabling_sync_the_feature", |
| base::Value(allow_enabling_sync_the_feature)); |
| |
| if (service->HasUnrecoverableError()) { |
| std::string unrecoverable_error_message = |
| "Unrecoverable error detected at " + |
| service->GetUnrecoverableErrorLocationForDebugging().ToString() + ": " + |
| service->GetUnrecoverableErrorMessageForDebugging(); |
| about_info.Set("unrecoverable_error_message", |
| base::Value(unrecoverable_error_message)); |
| } |
| |
| about_info.Set("type_status", TypeStatusMapToValueList( |
| service->GetTypeStatusMapForDebugging())); |
| |
| return about_info; |
| } |
| |
| } // namespace syncer::sync_ui_util |