Infobar material design refresh: bg color
[chromium-blink-merge.git] / components / sync_driver / about_sync_util.cc
blob010d627ea49bb2338e39a2aae640f473298c7555
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.
5 #include "components/sync_driver/about_sync_util.h"
7 #include <string>
9 #include "base/location.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/values.h"
13 #include "components/signin/core/browser/signin_manager_base.h"
14 #include "components/sync_driver/sync_service.h"
15 #include "components/version_info/version_info.h"
16 #include "sync/api/time.h"
17 #include "sync/internal_api/public/engine/sync_status.h"
18 #include "sync/internal_api/public/sessions/sync_session_snapshot.h"
19 #include "sync/internal_api/public/util/sync_string_conversions.h"
20 #include "sync/protocol/proto_enum_conversions.h"
22 using base::DictionaryValue;
23 using base::ListValue;
25 const char kIdentityTitle[] = "Identity";
26 const char kDetailsKey[] = "details";
28 namespace {
30 // Creates a 'section' for display on about:sync, consisting of a title and a
31 // list of fields. Returns a pointer to the new section. Note that
32 // |parent_list|, not the caller, owns the newly added section.
33 base::ListValue* AddSection(base::ListValue* parent_list,
34 const std::string& title) {
35 base::DictionaryValue* section = new base::DictionaryValue();
36 base::ListValue* section_contents = new base::ListValue();
37 section->SetString("title", title);
38 section->Set("data", section_contents);
39 section->SetBoolean("is_sensitive", false);
40 parent_list->Append(section);
41 return section_contents;
44 // Same as AddSection, but for data that should be elided when dumped into text
45 // form and posted in a public forum (e.g. unique identifiers).
46 base::ListValue* AddSensitiveSection(base::ListValue* parent_list,
47 const std::string& title) {
48 base::DictionaryValue* section = new base::DictionaryValue();
49 base::ListValue* section_contents = new base::ListValue();
50 section->SetString("title", title);
51 section->Set("data", section_contents);
52 section->SetBoolean("is_sensitive", true);
53 parent_list->Append(section);
54 return section_contents;
57 // The following helper classes help manage the about:sync fields which will be
58 // populated in method in ConstructAboutInformation.
60 // Each instance of one of thse classes indicates a field in about:sync. Each
61 // field will be serialized to a DictionaryValue with entries for 'stat_name',
62 // 'stat_value' and 'is_valid'.
64 class StringSyncStat {
65 public:
66 StringSyncStat(base::ListValue* section, const std::string& key);
67 void SetValue(const std::string& value);
68 void SetValue(const base::string16& value);
70 private:
71 // Owned by the |section| passed in during construction.
72 base::DictionaryValue* stat_;
75 StringSyncStat::StringSyncStat(base::ListValue* section,
76 const std::string& key) {
77 stat_ = new base::DictionaryValue();
78 stat_->SetString("stat_name", key);
79 stat_->SetString("stat_value", "Uninitialized");
80 stat_->SetBoolean("is_valid", false);
81 section->Append(stat_);
84 void StringSyncStat::SetValue(const std::string& value) {
85 stat_->SetString("stat_value", value);
86 stat_->SetBoolean("is_valid", true);
89 void StringSyncStat::SetValue(const base::string16& value) {
90 stat_->SetString("stat_value", value);
91 stat_->SetBoolean("is_valid", true);
94 class BoolSyncStat {
95 public:
96 BoolSyncStat(base::ListValue* section, const std::string& key);
97 void SetValue(bool value);
99 private:
100 // Owned by the |section| passed in during construction.
101 base::DictionaryValue* stat_;
104 BoolSyncStat::BoolSyncStat(base::ListValue* section, const std::string& key) {
105 stat_ = new base::DictionaryValue();
106 stat_->SetString("stat_name", key);
107 stat_->SetBoolean("stat_value", false);
108 stat_->SetBoolean("is_valid", false);
109 section->Append(stat_);
112 void BoolSyncStat::SetValue(bool value) {
113 stat_->SetBoolean("stat_value", value);
114 stat_->SetBoolean("is_valid", true);
117 class IntSyncStat {
118 public:
119 IntSyncStat(base::ListValue* section, const std::string& key);
120 void SetValue(int value);
122 private:
123 // Owned by the |section| passed in during construction.
124 base::DictionaryValue* stat_;
127 IntSyncStat::IntSyncStat(base::ListValue* section, const std::string& key) {
128 stat_ = new base::DictionaryValue();
129 stat_->SetString("stat_name", key);
130 stat_->SetInteger("stat_value", 0);
131 stat_->SetBoolean("is_valid", false);
132 section->Append(stat_);
135 void IntSyncStat::SetValue(int value) {
136 stat_->SetInteger("stat_value", value);
137 stat_->SetBoolean("is_valid", true);
140 // Returns a string describing the chrome version environment. Version format:
141 // <Build Info> <OS> <Version number> (<Last change>)<channel or "-devel">
142 // If version information is unavailable, returns "invalid."
143 // TODO(zea): this approximately matches MakeUserAgentForSyncApi in
144 // sync_backend_host.cc. Unify the two if possible.
145 std::string GetVersionString(version_info::Channel channel) {
146 // Build a version string that matches MakeUserAgentForSyncApi with the
147 // addition of channel info and proper OS names.
148 // chrome::GetChannelString() returns empty string for stable channel or
149 // unofficial builds, the channel string otherwise. We want to have "-devel"
150 // for unofficial builds only.
151 std::string version_modifier = version_info::GetChannelString(channel);
152 if (version_modifier.empty()) {
153 if (channel != version_info::Channel::STABLE) {
154 version_modifier = "-devel";
156 } else {
157 version_modifier = " " + version_modifier;
159 return version_info::GetProductName() + " " + version_info::GetOSType() +
160 " " + version_info::GetVersionNumber() + " (" +
161 version_info::GetLastChange() + ")" + version_modifier;
164 std::string GetTimeStr(base::Time time, const std::string& default_msg) {
165 std::string time_str;
166 if (time.is_null())
167 time_str = default_msg;
168 else
169 time_str = syncer::GetTimeDebugString(time);
170 return time_str;
173 std::string GetConnectionStatus(
174 const sync_driver::SyncService::SyncTokenStatus& status) {
175 std::string message;
176 switch (status.connection_status) {
177 case syncer::CONNECTION_NOT_ATTEMPTED:
178 base::StringAppendF(&message, "not attempted");
179 break;
180 case syncer::CONNECTION_OK:
181 base::StringAppendF(
182 &message, "OK since %s",
183 GetTimeStr(status.connection_status_update_time, "n/a").c_str());
184 break;
185 case syncer::CONNECTION_AUTH_ERROR:
186 base::StringAppendF(
187 &message, "auth error since %s",
188 GetTimeStr(status.connection_status_update_time, "n/a").c_str());
189 break;
190 case syncer::CONNECTION_SERVER_ERROR:
191 base::StringAppendF(
192 &message, "server error since %s",
193 GetTimeStr(status.connection_status_update_time, "n/a").c_str());
194 break;
195 default:
196 NOTREACHED();
198 return message;
201 } // namespace
203 namespace sync_ui_util {
205 // This function both defines the structure of the message to be returned and
206 // its contents. Most of the message consists of simple fields in about:sync
207 // which are grouped into sections and populated with the help of the SyncStat
208 // classes defined above.
209 scoped_ptr<base::DictionaryValue> ConstructAboutInformation(
210 sync_driver::SyncService* service,
211 SigninManagerBase* signin,
212 version_info::Channel channel) {
213 scoped_ptr<base::DictionaryValue> about_info(new base::DictionaryValue());
215 // 'details': A list of sections.
216 base::ListValue* stats_list = new base::ListValue();
218 // The following lines define the sections and their fields. For each field,
219 // a class is instantiated, which allows us to reference the fields in
220 // 'setter' code later on in this function.
221 base::ListValue* section_summary = AddSection(stats_list, "Summary");
222 StringSyncStat summary_string(section_summary, "Summary");
224 base::ListValue* section_version = AddSection(stats_list, "Version Info");
225 StringSyncStat client_version(section_version, "Client Version");
226 StringSyncStat server_url(section_version, "Server URL");
228 base::ListValue* section_identity =
229 AddSensitiveSection(stats_list, kIdentityTitle);
230 StringSyncStat sync_id(section_identity, "Sync Client ID");
231 StringSyncStat invalidator_id(section_identity, "Invalidator Client ID");
232 StringSyncStat username(section_identity, "Username");
234 base::ListValue* section_credentials = AddSection(stats_list, "Credentials");
235 StringSyncStat request_token_time(section_credentials, "Requested Token");
236 StringSyncStat receive_token_time(section_credentials, "Received Token");
237 StringSyncStat token_request_status(section_credentials,
238 "Token Request Status");
239 StringSyncStat next_token_request(section_credentials,
240 "Next Token Request");
242 base::ListValue* section_local = AddSection(stats_list, "Local State");
243 StringSyncStat server_connection(section_local,
244 "Server Connection");
245 StringSyncStat last_synced(section_local, "Last Synced");
246 BoolSyncStat is_setup_complete(section_local,
247 "Sync First-Time Setup Complete");
248 StringSyncStat backend_initialization(section_local,
249 "Sync Backend Initialization");
250 BoolSyncStat is_syncing(section_local, "Syncing");
252 base::ListValue* section_network = AddSection(stats_list, "Network");
253 BoolSyncStat is_throttled(section_network, "Throttled");
254 StringSyncStat retry_time(section_network, "Retry time (maybe stale)");
255 BoolSyncStat are_notifications_enabled(section_network,
256 "Notifications Enabled");
258 base::ListValue* section_encryption = AddSection(stats_list, "Encryption");
259 BoolSyncStat is_using_explicit_passphrase(section_encryption,
260 "Explicit Passphrase");
261 BoolSyncStat is_passphrase_required(section_encryption,
262 "Passphrase Required");
263 BoolSyncStat is_cryptographer_ready(section_encryption,
264 "Cryptographer Ready");
265 BoolSyncStat has_pending_keys(section_encryption,
266 "Cryptographer Has Pending Keys");
267 StringSyncStat encrypted_types(section_encryption, "Encrypted Types");
268 BoolSyncStat has_keystore_key(section_encryption, "Has Keystore Key");
269 StringSyncStat keystore_migration_time(section_encryption,
270 "Keystore Migration Time");
271 StringSyncStat passphrase_type(section_encryption,
272 "Passphrase Type");
273 StringSyncStat passphrase_time(section_encryption,
274 "Passphrase Time");
276 base::ListValue* section_last_session = AddSection(
277 stats_list, "Status from Last Completed Session");
278 StringSyncStat session_source(section_last_session, "Sync Source");
279 StringSyncStat get_key_result(section_last_session, "GetKey Step Result");
280 StringSyncStat download_result(section_last_session, "Download Step Result");
281 StringSyncStat commit_result(section_last_session, "Commit Step Result");
283 base::ListValue* section_counters = AddSection(stats_list, "Running Totals");
284 IntSyncStat notifications_received(section_counters,
285 "Notifications Received");
286 IntSyncStat updates_received(section_counters, "Updates Downloaded");
287 IntSyncStat tombstone_updates(section_counters, "Tombstone Updates");
288 IntSyncStat reflected_updates(section_counters, "Reflected Updates");
289 IntSyncStat successful_commits(section_counters, "Successful Commits");
290 IntSyncStat conflicts_resolved_local_wins(section_counters,
291 "Conflicts Resolved: Client Wins");
292 IntSyncStat conflicts_resolved_server_wins(section_counters,
293 "Conflicts Resolved: Server Wins");
295 base::ListValue *section_this_cycle = AddSection(stats_list,
296 "Transient Counters (this cycle)");
297 IntSyncStat encryption_conflicts(section_this_cycle, "Encryption Conflicts");
298 IntSyncStat hierarchy_conflicts(section_this_cycle, "Hierarchy Conflicts");
299 IntSyncStat server_conflicts(section_this_cycle, "Server Conflicts");
300 IntSyncStat committed_items(section_this_cycle, "Committed Items");
302 base::ListValue* section_that_cycle = AddSection(
303 stats_list, "Transient Counters (last cycle of last completed session)");
304 IntSyncStat updates_downloaded(section_that_cycle, "Updates Downloaded");
305 IntSyncStat committed_count(section_that_cycle, "Committed Count");
306 IntSyncStat entries(section_that_cycle, "Entries");
308 base::ListValue* section_nudge_info = AddSection(
309 stats_list, "Nudge Source Counters");
310 IntSyncStat nudge_source_notification(
311 section_nudge_info, "Server Invalidations");
312 IntSyncStat nudge_source_local(section_nudge_info, "Local Changes");
313 IntSyncStat nudge_source_local_refresh(section_nudge_info, "Local Refreshes");
315 // This list of sections belongs in the 'details' field of the returned
316 // message.
317 about_info->Set(kDetailsKey, stats_list);
319 // Populate all the fields we declared above.
320 client_version.SetValue(GetVersionString(channel));
322 if (!service) {
323 summary_string.SetValue("Sync service does not exist");
324 return about_info.Pass();
327 syncer::SyncStatus full_status;
328 bool is_status_valid = service->QueryDetailedSyncStatus(&full_status);
329 bool sync_active = service->IsSyncActive();
330 const syncer::sessions::SyncSessionSnapshot& snapshot =
331 service->GetLastSessionSnapshot();
333 if (is_status_valid)
334 summary_string.SetValue(service->QuerySyncStatusSummaryString());
336 server_url.SetValue(service->sync_service_url().spec());
338 if (is_status_valid && !full_status.sync_id.empty())
339 sync_id.SetValue(full_status.sync_id);
340 if (is_status_valid && !full_status.invalidator_client_id.empty())
341 invalidator_id.SetValue(full_status.invalidator_client_id);
342 if (signin)
343 username.SetValue(signin->GetAuthenticatedAccountInfo().email);
345 const sync_driver::SyncService::SyncTokenStatus& token_status =
346 service->GetSyncTokenStatus();
347 server_connection.SetValue(GetConnectionStatus(token_status));
348 request_token_time.SetValue(GetTimeStr(token_status.token_request_time,
349 "n/a"));
350 receive_token_time.SetValue(GetTimeStr(token_status.token_receive_time,
351 "n/a"));
352 std::string err = token_status.last_get_token_error.error_message();
353 token_request_status.SetValue(err.empty() ? "OK" : err);
354 next_token_request.SetValue(
355 GetTimeStr(token_status.next_token_request_time, "not scheduled"));
357 last_synced.SetValue(service->GetLastSyncedTimeString());
358 is_setup_complete.SetValue(service->HasSyncSetupCompleted());
359 backend_initialization.SetValue(
360 service->GetBackendInitializationStateString());
361 if (is_status_valid) {
362 is_syncing.SetValue(full_status.syncing);
363 retry_time.SetValue(GetTimeStr(full_status.retry_time,
364 "Scheduler is not in backoff or throttled"));
367 if (snapshot.is_initialized())
368 is_throttled.SetValue(snapshot.is_silenced());
369 if (is_status_valid) {
370 are_notifications_enabled.SetValue(
371 full_status.notifications_enabled);
374 if (sync_active) {
375 is_using_explicit_passphrase.SetValue(
376 service->IsUsingSecondaryPassphrase());
377 is_passphrase_required.SetValue(service->IsPassphraseRequired());
378 passphrase_time.SetValue(
379 GetTimeStr(service->GetExplicitPassphraseTime(), "No Passphrase Time"));
381 if (is_status_valid) {
382 is_cryptographer_ready.SetValue(full_status.cryptographer_ready);
383 has_pending_keys.SetValue(full_status.crypto_has_pending_keys);
384 encrypted_types.SetValue(
385 ModelTypeSetToString(full_status.encrypted_types));
386 has_keystore_key.SetValue(full_status.has_keystore_key);
387 keystore_migration_time.SetValue(
388 GetTimeStr(full_status.keystore_migration_time, "Not Migrated"));
389 passphrase_type.SetValue(
390 PassphraseTypeToString(full_status.passphrase_type));
393 if (snapshot.is_initialized()) {
394 if (snapshot.legacy_updates_source() !=
395 sync_pb::GetUpdatesCallerInfo::UNKNOWN) {
396 session_source.SetValue(
397 syncer::GetUpdatesSourceString(snapshot.legacy_updates_source()));
399 get_key_result.SetValue(
400 GetSyncerErrorString(
401 snapshot.model_neutral_state().last_get_key_result));
402 download_result.SetValue(
403 GetSyncerErrorString(
404 snapshot.model_neutral_state().last_download_updates_result));
405 commit_result.SetValue(
406 GetSyncerErrorString(
407 snapshot.model_neutral_state().commit_result));
410 if (is_status_valid) {
411 notifications_received.SetValue(full_status.notifications_received);
412 updates_received.SetValue(full_status.updates_received);
413 tombstone_updates.SetValue(full_status.tombstone_updates_received);
414 reflected_updates.SetValue(full_status.reflected_updates_received);
415 successful_commits.SetValue(full_status.num_commits_total);
416 conflicts_resolved_local_wins.SetValue(
417 full_status.num_local_overwrites_total);
418 conflicts_resolved_server_wins.SetValue(
419 full_status.num_server_overwrites_total);
422 if (is_status_valid) {
423 encryption_conflicts.SetValue(full_status.encryption_conflicts);
424 hierarchy_conflicts.SetValue(full_status.hierarchy_conflicts);
425 server_conflicts.SetValue(full_status.server_conflicts);
426 committed_items.SetValue(full_status.committed_count);
429 if (is_status_valid) {
430 nudge_source_notification.SetValue(full_status.nudge_source_notification);
431 nudge_source_local.SetValue(full_status.nudge_source_local);
432 nudge_source_local_refresh.SetValue(full_status.nudge_source_local_refresh);
435 if (snapshot.is_initialized()) {
436 updates_downloaded.SetValue(
437 snapshot.model_neutral_state().num_updates_downloaded_total);
438 committed_count.SetValue(
439 snapshot.model_neutral_state().num_successful_commits);
440 entries.SetValue(snapshot.num_entries());
443 // The values set from this point onwards do not belong in the
444 // details list.
446 // We don't need to check is_status_valid here.
447 // full_status.sync_protocol_error is exported directly from the
448 // ProfileSyncService, even if the backend doesn't exist.
449 const bool actionable_error_detected =
450 full_status.sync_protocol_error.error_type != syncer::UNKNOWN_ERROR &&
451 full_status.sync_protocol_error.error_type != syncer::SYNC_SUCCESS;
453 about_info->SetBoolean("actionable_error_detected",
454 actionable_error_detected);
456 // NOTE: We won't bother showing any of the following values unless
457 // actionable_error_detected is set.
459 base::ListValue* actionable_error = new base::ListValue();
460 about_info->Set("actionable_error", actionable_error);
462 StringSyncStat error_type(actionable_error, "Error Type");
463 StringSyncStat action(actionable_error, "Action");
464 StringSyncStat url(actionable_error, "URL");
465 StringSyncStat description(actionable_error, "Error Description");
467 if (actionable_error_detected) {
468 error_type.SetValue(syncer::GetSyncErrorTypeString(
469 full_status.sync_protocol_error.error_type));
470 action.SetValue(syncer::GetClientActionString(
471 full_status.sync_protocol_error.action));
472 url.SetValue(full_status.sync_protocol_error.url);
473 description.SetValue(full_status.sync_protocol_error.error_description);
476 about_info->SetBoolean("unrecoverable_error_detected",
477 service->HasUnrecoverableError());
479 if (service->HasUnrecoverableError()) {
480 tracked_objects::Location loc(service->unrecoverable_error_location());
481 std::string location_str;
482 loc.Write(true, true, &location_str);
483 std::string unrecoverable_error_message =
484 "Unrecoverable error detected at " + location_str +
485 ": " + service->unrecoverable_error_message();
486 about_info->SetString("unrecoverable_error_message",
487 unrecoverable_error_message);
490 about_info->Set("type_status", service->GetTypeStatusMap());
492 return about_info.Pass();
495 } // namespace sync_ui_util