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 "chrome/browser/sync/about_sync_util.h"
9 #include "base/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"
18 using base::DictionaryValue
;
19 using base::ListValue
;
21 const char kCredentialsTitle
[] = "Credentials";
22 const char kDetailsKey
[] = "details";
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
;
38 // The following helper classes help manage the about:sync fields which will be
39 // populated in method in ConstructAboutInformation.
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'.
45 class StringSyncStat
{
47 StringSyncStat(ListValue
* section
, const std::string
& key
);
48 void SetValue(const std::string
& value
);
49 void SetValue(const string16
& value
);
52 // Owned by the |section| passed in during construction.
53 DictionaryValue
* stat_
;
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_
);
64 void StringSyncStat::SetValue(const std::string
& value
) {
65 stat_
->SetString("stat_value", value
);
66 stat_
->SetBoolean("is_valid", true);
69 void StringSyncStat::SetValue(const string16
& value
) {
70 stat_
->SetString("stat_value", value
);
71 stat_
->SetBoolean("is_valid", true);
76 BoolSyncStat(ListValue
* section
, const std::string
& key
);
77 void SetValue(bool value
);
80 // Owned by the |section| passed in during construction.
81 DictionaryValue
* stat_
;
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_
);
92 void BoolSyncStat::SetValue(bool value
) {
93 stat_
->SetBoolean("stat_value", value
);
94 stat_
->SetBoolean("is_valid", true);
99 IntSyncStat(ListValue
* section
, const std::string
& key
);
100 void SetValue(int value
);
103 // Owned by the |section| passed in during construction.
104 DictionaryValue
* stat_
;
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_
);
115 void IntSyncStat::SetValue(int value
) {
116 stat_
->SetInteger("stat_value", value
);
117 stat_
->SetBoolean("is_valid", true);
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())
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";
142 version_modifier
= " " + version_modifier
;
144 return chrome_version
.Name() + " " + chrome_version
.OSType() + " " +
145 chrome_version
.Version() + " (" + chrome_version
.LastChange() + ")" +
149 std::string
GetTimeStr(base::Time time
, const std::string
& default_msg
) {
150 std::string time_str
;
152 time_str
= default_msg
;
154 time_str
= syncer::GetTimeDebugString(time
);
160 namespace sync_ui_util
{
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.
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");
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");
181 ListValue
* section_credentials
= AddSection(stats_list
, kCredentialsTitle
);
182 StringSyncStat
client_id(section_credentials
, "Client ID");
183 StringSyncStat
username(section_credentials
, "Username");
184 BoolSyncStat
is_token_available(section_credentials
, "Sync Token Available");
186 ListValue
* section_local
= AddSection(stats_list
, "Local State");
187 StringSyncStat
last_synced(section_local
, "Last Synced");
188 BoolSyncStat
is_setup_complete(section_local
,
189 "Sync First-Time Setup Complete");
190 BoolSyncStat
is_backend_initialized(section_local
,
191 "Sync Backend Initialized");
192 BoolSyncStat
is_download_complete(section_local
, "Initial Download Complete");
193 BoolSyncStat
is_syncing(section_local
, "Syncing");
195 ListValue
* section_network
= AddSection(stats_list
, "Network");
196 BoolSyncStat
is_throttled(section_network
, "Throttled");
197 BoolSyncStat
are_notifications_enabled(section_network
,
198 "Notifications Enabled");
200 ListValue
* section_encryption
= AddSection(stats_list
, "Encryption");
201 BoolSyncStat
is_using_explicit_passphrase(section_encryption
,
202 "Explicit Passphrase");
203 BoolSyncStat
is_passphrase_required(section_encryption
,
204 "Passphrase Required");
205 BoolSyncStat
is_cryptographer_ready(section_encryption
,
206 "Cryptographer Ready");
207 BoolSyncStat
has_pending_keys(section_encryption
,
208 "Cryptographer Has Pending Keys");
209 StringSyncStat
encrypted_types(section_encryption
, "Encrypted Types");
210 BoolSyncStat
has_keystore_key(section_encryption
, "Has Keystore Key");
211 StringSyncStat
keystore_migration_time(section_encryption
,
212 "Keystore Migration Time");
213 StringSyncStat
passphrase_type(section_encryption
,
215 StringSyncStat
passphrase_time(section_encryption
,
218 ListValue
* section_last_session
= AddSection(
219 stats_list
, "Status from Last Completed Session");
220 StringSyncStat
session_source(section_last_session
, "Sync Source");
221 StringSyncStat
get_key_result(section_last_session
, "GetKey Step Result");
222 StringSyncStat
download_result(section_last_session
, "Download Step Result");
223 StringSyncStat
commit_result(section_last_session
, "Commit Step Result");
225 ListValue
* section_counters
= AddSection(stats_list
, "Running Totals");
226 IntSyncStat
notifications_received(section_counters
,
227 "Notifications Received");
228 IntSyncStat
empty_get_updates(section_counters
, "Cycles Without Updates");
229 IntSyncStat
non_empty_get_updates(section_counters
, "Cycles With Updated");
230 IntSyncStat
sync_cycles_without_commits(section_counters
,
231 "Cycles Without Commits");
232 IntSyncStat
sync_cycles_with_commits(section_counters
, "Cycles With Commits");
233 IntSyncStat
useless_sync_cycles(section_counters
,
234 "Cycles Without Commits or Updates");
235 IntSyncStat
useful_sync_cycles(section_counters
,
236 "Cycles With Commit or Update");
237 IntSyncStat
updates_received(section_counters
, "Updates Downloaded");
238 IntSyncStat
tombstone_updates(section_counters
, "Tombstone Updates");
239 IntSyncStat
reflected_updates(section_counters
, "Reflected Updates");
240 IntSyncStat
successful_commits(section_counters
, "Successful Commits");
241 IntSyncStat
conflicts_resolved_local_wins(section_counters
,
242 "Conflicts Resolved: Client Wins");
243 IntSyncStat
conflicts_resolved_server_wins(section_counters
,
244 "Conflicts Resolved: Server Wins");
246 ListValue
*section_this_cycle
= AddSection(stats_list
,
247 "Transient Counters (this cycle)");
248 IntSyncStat
encryption_conflicts(section_this_cycle
, "Encryption Conflicts");
249 IntSyncStat
hierarchy_conflicts(section_this_cycle
, "Hierarchy Conflicts");
250 IntSyncStat
server_conflicts(section_this_cycle
, "Server Conflicts");
251 IntSyncStat
committed_items(section_this_cycle
, "Committed Items");
252 IntSyncStat
updates_remaining(section_this_cycle
, "Updates Remaining");
254 ListValue
* section_that_cycle
= AddSection(
255 stats_list
, "Transient Counters (last cycle of last completed session)");
256 IntSyncStat
updates_downloaded(section_that_cycle
, "Updates Downloaded");
257 IntSyncStat
committed_count(section_that_cycle
, "Committed Count");
258 IntSyncStat
entries(section_that_cycle
, "Entries");
260 ListValue
* section_nudge_info
= AddSection(
261 stats_list
, "Nudge Source Counters");
262 IntSyncStat
nudge_source_notification(
263 section_nudge_info
, "Server Invalidations");
264 IntSyncStat
nudge_source_local(section_nudge_info
, "Local Changes");
265 IntSyncStat
nudge_source_local_refresh(section_nudge_info
, "Local Refreshes");
267 // This list of sections belongs in the 'details' field of the returned
269 about_info
->Set(kDetailsKey
, stats_list
);
271 // Populate all the fields we declared above.
272 client_version
.SetValue(GetVersionString());
275 summary_string
.SetValue("Sync service does not exist");
276 return about_info
.Pass();
279 syncer::SyncStatus full_status
;
280 bool is_status_valid
= service
->QueryDetailedSyncStatus(&full_status
);
281 bool sync_initialized
= service
->sync_initialized();
282 const syncer::sessions::SyncSessionSnapshot
& snapshot
=
284 service
->GetLastSessionSnapshot() :
285 syncer::sessions::SyncSessionSnapshot();
288 summary_string
.SetValue(service
->QuerySyncStatusSummary());
290 server_url
.SetValue(service
->sync_service_url().spec());
292 if (is_status_valid
&& !full_status
.unique_id
.empty())
293 client_id
.SetValue(full_status
.unique_id
);
294 if (service
->signin())
295 username
.SetValue(service
->signin()->GetAuthenticatedUsername());
296 is_token_available
.SetValue(service
->IsSyncTokenAvailable());
298 last_synced
.SetValue(service
->GetLastSyncedTimeString());
299 is_setup_complete
.SetValue(service
->HasSyncSetupCompleted());
300 is_backend_initialized
.SetValue(sync_initialized
);
301 if (is_status_valid
) {
302 is_download_complete
.SetValue(full_status
.initial_sync_ended
);
303 is_syncing
.SetValue(full_status
.syncing
);
306 if (snapshot
.is_initialized())
307 is_throttled
.SetValue(snapshot
.is_silenced());
308 if (is_status_valid
) {
309 are_notifications_enabled
.SetValue(
310 full_status
.notifications_enabled
);
313 if (sync_initialized
) {
314 is_using_explicit_passphrase
.SetValue(
315 service
->IsUsingSecondaryPassphrase());
316 is_passphrase_required
.SetValue(service
->IsPassphraseRequired());
317 passphrase_time
.SetValue(
318 GetTimeStr(service
->GetExplicitPassphraseTime(), "No Passphrase Time"));
320 if (is_status_valid
) {
321 is_cryptographer_ready
.SetValue(full_status
.cryptographer_ready
);
322 has_pending_keys
.SetValue(full_status
.crypto_has_pending_keys
);
323 encrypted_types
.SetValue(
324 ModelTypeSetToString(full_status
.encrypted_types
));
325 has_keystore_key
.SetValue(full_status
.has_keystore_key
);
326 keystore_migration_time
.SetValue(
327 GetTimeStr(full_status
.keystore_migration_time
, "Not Migrated"));
328 passphrase_type
.SetValue(
329 PassphraseTypeToString(full_status
.passphrase_type
));
332 if (snapshot
.is_initialized()) {
333 session_source
.SetValue(
334 syncer::GetUpdatesSourceString(snapshot
.source().updates_source
));
335 get_key_result
.SetValue(
336 GetSyncerErrorString(
337 snapshot
.model_neutral_state().last_get_key_result
));
338 download_result
.SetValue(
339 GetSyncerErrorString(
340 snapshot
.model_neutral_state().last_download_updates_result
));
341 commit_result
.SetValue(
342 GetSyncerErrorString(
343 snapshot
.model_neutral_state().commit_result
));
346 if (is_status_valid
) {
347 notifications_received
.SetValue(full_status
.notifications_received
);
348 empty_get_updates
.SetValue(full_status
.empty_get_updates
);
349 non_empty_get_updates
.SetValue(full_status
.nonempty_get_updates
);
350 sync_cycles_without_commits
.SetValue(
351 full_status
.sync_cycles_without_commits
);
352 sync_cycles_with_commits
.SetValue(
353 full_status
.sync_cycles_with_commits
);
354 useless_sync_cycles
.SetValue(full_status
.useless_sync_cycles
);
355 useful_sync_cycles
.SetValue(full_status
.useful_sync_cycles
);
356 updates_received
.SetValue(full_status
.updates_received
);
357 tombstone_updates
.SetValue(full_status
.tombstone_updates_received
);
358 reflected_updates
.SetValue(full_status
.reflected_updates_received
);
359 successful_commits
.SetValue(full_status
.num_commits_total
);
360 conflicts_resolved_local_wins
.SetValue(
361 full_status
.num_local_overwrites_total
);
362 conflicts_resolved_server_wins
.SetValue(
363 full_status
.num_server_overwrites_total
);
366 if (is_status_valid
) {
367 encryption_conflicts
.SetValue(full_status
.encryption_conflicts
);
368 hierarchy_conflicts
.SetValue(full_status
.hierarchy_conflicts
);
369 server_conflicts
.SetValue(full_status
.server_conflicts
);
370 committed_items
.SetValue(full_status
.committed_count
);
371 updates_remaining
.SetValue(full_status
.updates_available
);
374 if (is_status_valid
) {
375 nudge_source_notification
.SetValue(full_status
.nudge_source_notification
);
376 nudge_source_local
.SetValue(full_status
.nudge_source_local
);
377 nudge_source_local_refresh
.SetValue(full_status
.nudge_source_local_refresh
);
380 if (snapshot
.is_initialized()) {
381 updates_downloaded
.SetValue(
382 snapshot
.model_neutral_state().num_updates_downloaded_total
);
383 committed_count
.SetValue(
384 snapshot
.model_neutral_state().num_successful_commits
);
385 entries
.SetValue(snapshot
.num_entries());
388 // The values set from this point onwards do not belong in the
391 // We don't need to check is_status_valid here.
392 // full_status.sync_protocol_error is exported directly from the
393 // ProfileSyncService, even if the backend doesn't exist.
394 const bool actionable_error_detected
=
395 full_status
.sync_protocol_error
.error_type
!= syncer::UNKNOWN_ERROR
&&
396 full_status
.sync_protocol_error
.error_type
!= syncer::SYNC_SUCCESS
;
398 about_info
->SetBoolean("actionable_error_detected",
399 actionable_error_detected
);
401 // NOTE: We won't bother showing any of the following values unless
402 // actionable_error_detected is set.
404 ListValue
* actionable_error
= new ListValue();
405 about_info
->Set("actionable_error", actionable_error
);
407 StringSyncStat
error_type(actionable_error
, "Error Type");
408 StringSyncStat
action(actionable_error
, "Action");
409 StringSyncStat
url(actionable_error
, "URL");
410 StringSyncStat
description(actionable_error
, "Error Description");
412 if (actionable_error_detected
) {
413 error_type
.SetValue(syncer::GetSyncErrorTypeString(
414 full_status
.sync_protocol_error
.error_type
));
415 action
.SetValue(syncer::GetClientActionString(
416 full_status
.sync_protocol_error
.action
));
417 url
.SetValue(full_status
.sync_protocol_error
.url
);
418 description
.SetValue(full_status
.sync_protocol_error
.error_description
);
421 about_info
->SetBoolean("unrecoverable_error_detected",
422 service
->HasUnrecoverableError());
424 if (service
->HasUnrecoverableError()) {
425 tracked_objects::Location
loc(service
->unrecoverable_error_location());
426 std::string location_str
;
427 loc
.Write(true, true, &location_str
);
428 std::string unrecoverable_error_message
=
429 "Unrecoverable error detected at " + location_str
+
430 ": " + service
->unrecoverable_error_message();
431 about_info
->SetString("unrecoverable_error_message",
432 unrecoverable_error_message
);
435 about_info
->Set("type_status", service
->GetTypeStatusMap());
437 return about_info
.Pass();
440 } // namespace sync_ui_util