Stack sampling profiler: add fire-and-forget interface
[chromium-blink-merge.git] / components / signin / core / browser / about_signin_internals.cc
blob7c139a0580332ce297e2743c0dcb957e194113de
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/signin/core/browser/about_signin_internals.h"
7 #include "base/command_line.h"
8 #include "base/hash.h"
9 #include "base/i18n/time_formatting.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/trace_event/trace_event.h"
16 #include "components/signin/core/browser/account_tracker_service.h"
17 #include "components/signin/core/browser/profile_oauth2_token_service.h"
18 #include "components/signin/core/browser/signin_client.h"
19 #include "components/signin/core/browser/signin_internals_util.h"
20 #include "components/signin/core/browser/signin_manager.h"
21 #include "components/signin/core/common/profile_management_switches.h"
22 #include "components/signin/core/common/signin_switches.h"
24 using base::Time;
25 using namespace signin_internals_util;
27 namespace {
29 std::string GetTimeStr(base::Time time) {
30 return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time));
33 base::ListValue* AddSection(base::ListValue* parent_list,
34 const std::string& title) {
35 scoped_ptr<base::DictionaryValue> section(new base::DictionaryValue());
36 base::ListValue* section_contents = new base::ListValue();
38 section->SetString("title", title);
39 section->Set("data", section_contents);
40 parent_list->Append(section.release());
41 return section_contents;
44 void AddSectionEntry(base::ListValue* section_list,
45 const std::string& field_name,
46 const std::string& field_status,
47 const std::string& field_time = "") {
48 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
49 entry->SetString("label", field_name);
50 entry->SetString("status", field_status);
51 entry->SetString("time", field_time);
52 section_list->Append(entry.release());
55 void AddCookieEntry(base::ListValue* accounts_list,
56 const std::string& field_email,
57 const std::string& field_gaia_id,
58 const std::string& field_valid) {
59 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
60 entry->SetString("email", field_email);
61 entry->SetString("gaia_id", field_gaia_id);
62 entry->SetString("valid", field_valid);
63 accounts_list->Append(entry.release());
66 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
67 switch (field) {
68 case ACCOUNT_ID:
69 return "Account Id";
70 case GAIA_ID:
71 return "Gaia Id";
72 case USERNAME:
73 return "Username";
74 case UNTIMED_FIELDS_END:
75 NOTREACHED();
76 return std::string();
78 NOTREACHED();
79 return std::string();
82 #if !defined (OS_CHROMEOS)
83 std::string SigninStatusFieldToLabel(TimedSigninStatusField field) {
84 switch (field) {
85 case AUTHENTICATION_RESULT_RECEIVED:
86 return "Gaia Authentication Result";
87 case REFRESH_TOKEN_RECEIVED:
88 return "RefreshToken Received";
89 case SIGNIN_STARTED:
90 return "SigninManager Started";
91 case SIGNIN_COMPLETED:
92 return "SigninManager Completed";
93 case TIMED_FIELDS_END:
94 NOTREACHED();
95 return "Error";
97 NOTREACHED();
98 return "Error";
100 #endif // !defined (OS_CHROMEOS)
102 void SetPref(PrefService* prefs,
103 TimedSigninStatusField field,
104 const std::string& time,
105 const std::string& value) {
106 std::string value_pref = SigninStatusFieldToString(field) + ".value";
107 std::string time_pref = SigninStatusFieldToString(field) + ".time";
108 prefs->SetString(value_pref, value);
109 prefs->SetString(time_pref, time);
112 void GetPref(PrefService* prefs,
113 TimedSigninStatusField field,
114 std::string* time,
115 std::string* value) {
116 std::string value_pref = SigninStatusFieldToString(field) + ".value";
117 std::string time_pref = SigninStatusFieldToString(field) + ".time";
118 *value = prefs->GetString(value_pref);
119 *time = prefs->GetString(time_pref);
122 void ClearPref(PrefService* prefs, TimedSigninStatusField field) {
123 std::string value_pref = SigninStatusFieldToString(field) + ".value";
124 std::string time_pref = SigninStatusFieldToString(field) + ".time";
125 prefs->ClearPref(value_pref);
126 prefs->ClearPref(time_pref);
129 } // anonymous namespace
131 AboutSigninInternals::AboutSigninInternals(
132 ProfileOAuth2TokenService* token_service,
133 AccountTrackerService* account_tracker,
134 SigninManagerBase* signin_manager,
135 SigninErrorController* signin_error_controller,
136 GaiaCookieManagerService* cookie_manager_service)
137 : token_service_(token_service),
138 account_tracker_(account_tracker),
139 signin_manager_(signin_manager),
140 client_(NULL),
141 signin_error_controller_(signin_error_controller),
142 cookie_manager_service_(cookie_manager_service) {}
144 AboutSigninInternals::~AboutSigninInternals() {}
146 void AboutSigninInternals::AddSigninObserver(
147 AboutSigninInternals::Observer* observer) {
148 signin_observers_.AddObserver(observer);
151 void AboutSigninInternals::RemoveSigninObserver(
152 AboutSigninInternals::Observer* observer) {
153 signin_observers_.RemoveObserver(observer);
156 void AboutSigninInternals::NotifySigninValueChanged(
157 const TimedSigninStatusField& field,
158 const std::string& value) {
159 unsigned int field_index = field - TIMED_FIELDS_BEGIN;
160 DCHECK(field_index >= 0 &&
161 field_index < signin_status_.timed_signin_fields.size());
163 Time now = Time::NowFromSystemTime();
164 std::string time_as_str =
165 base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(now));
166 TimedSigninStatusValue timed_value(value, time_as_str);
168 signin_status_.timed_signin_fields[field_index] = timed_value;
170 // Also persist these values in the prefs.
171 SetPref(client_->GetPrefs(), field, value, time_as_str);
173 // If the user is restarting a sign in process, clear the fields that are
174 // to come.
175 if (field == AUTHENTICATION_RESULT_RECEIVED) {
176 ClearPref(client_->GetPrefs(), REFRESH_TOKEN_RECEIVED);
177 ClearPref(client_->GetPrefs(), SIGNIN_STARTED);
178 ClearPref(client_->GetPrefs(), SIGNIN_COMPLETED);
181 NotifyObservers();
184 void AboutSigninInternals::RefreshSigninPrefs() {
185 // Return if no client exists. Can occur in unit tests.
186 if (!client_)
187 return;
189 PrefService* pref_service = client_->GetPrefs();
190 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
191 std::string time_str;
192 std::string value_str;
193 GetPref(pref_service, static_cast<TimedSigninStatusField>(i),
194 &time_str, &value_str);
195 TimedSigninStatusValue value(value_str, time_str);
196 signin_status_.timed_signin_fields[i - TIMED_FIELDS_BEGIN] = value;
199 // TODO(rogerta): Get status and timestamps for oauth2 tokens.
201 NotifyObservers();
204 void AboutSigninInternals::Initialize(SigninClient* client) {
205 DCHECK(!client_);
206 client_ = client;
208 RefreshSigninPrefs();
210 signin_error_controller_->AddObserver(this);
211 signin_manager_->AddSigninDiagnosticsObserver(this);
212 token_service_->AddDiagnosticsObserver(this);
213 cookie_manager_service_->AddObserver(this);
216 void AboutSigninInternals::Shutdown() {
217 signin_error_controller_->RemoveObserver(this);
218 signin_manager_->RemoveSigninDiagnosticsObserver(this);
219 token_service_->RemoveDiagnosticsObserver(this);
220 cookie_manager_service_->RemoveObserver(this);
223 void AboutSigninInternals::NotifyObservers() {
224 if (!signin_observers_.might_have_observers())
225 return;
227 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
228 // fixed.
229 tracked_objects::ScopedTracker tracking_profile(
230 FROM_HERE_WITH_EXPLICIT_FUNCTION(
231 "422460 AboutSigninInternals::NotifyObservers"));
233 const std::string product_version = client_->GetProductVersion();
235 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
236 // fixed.
237 tracked_objects::ScopedTracker tracking_profile05(
238 FROM_HERE_WITH_EXPLICIT_FUNCTION(
239 "422460 AboutSigninInternals::NotifyObservers 0.5"));
241 scoped_ptr<base::DictionaryValue> signin_status_value =
242 signin_status_.ToValue(account_tracker_,
243 signin_manager_,
244 signin_error_controller_,
245 token_service_,
246 product_version);
248 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
249 // fixed.
250 tracked_objects::ScopedTracker tracking_profile1(
251 FROM_HERE_WITH_EXPLICIT_FUNCTION(
252 "422460 AboutSigninInternals::NotifyObservers1"));
254 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
255 signin_observers_,
256 OnSigninStateChanged(signin_status_value.get()));
259 scoped_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
260 return signin_status_.ToValue(account_tracker_,
261 signin_manager_,
262 signin_error_controller_,
263 token_service_,
264 client_->GetProductVersion()).Pass();
267 void AboutSigninInternals::OnAccessTokenRequested(
268 const std::string& account_id,
269 const std::string& consumer_id,
270 const OAuth2TokenService::ScopeSet& scopes) {
271 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
272 // fixed.
273 tracked_objects::ScopedTracker tracking_profile(
274 FROM_HERE_WITH_EXPLICIT_FUNCTION(
275 "422460 AboutSigninInternals::OnAccessTokenRequested"));
277 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
278 if (token) {
279 *token = TokenInfo(consumer_id, scopes);
280 } else {
281 token = new TokenInfo(consumer_id, scopes);
282 signin_status_.token_info_map[account_id].push_back(token);
285 NotifyObservers();
288 void AboutSigninInternals::OnFetchAccessTokenComplete(
289 const std::string& account_id,
290 const std::string& consumer_id,
291 const OAuth2TokenService::ScopeSet& scopes,
292 GoogleServiceAuthError error,
293 base::Time expiration_time) {
294 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
295 if (!token) {
296 DVLOG(1) << "Can't find token: " << account_id << ", " << consumer_id;
297 return;
300 token->receive_time = base::Time::Now();
301 token->error = error;
302 token->expiration_time = expiration_time;
304 NotifyObservers();
307 void AboutSigninInternals::OnTokenRemoved(
308 const std::string& account_id,
309 const OAuth2TokenService::ScopeSet& scopes) {
310 for (size_t i = 0; i < signin_status_.token_info_map[account_id].size();
311 ++i) {
312 TokenInfo* token = signin_status_.token_info_map[account_id][i];
313 if (token->scopes == scopes)
314 token->Invalidate();
316 NotifyObservers();
319 void AboutSigninInternals::OnRefreshTokenReceived(std::string status) {
320 NotifySigninValueChanged(REFRESH_TOKEN_RECEIVED, status);
323 void AboutSigninInternals::OnAuthenticationResultReceived(std::string status) {
324 NotifySigninValueChanged(AUTHENTICATION_RESULT_RECEIVED, status);
327 void AboutSigninInternals::OnErrorChanged() {
328 NotifyObservers();
331 void AboutSigninInternals::GoogleSigninFailed(
332 const GoogleServiceAuthError& error) {
333 NotifyObservers();
336 void AboutSigninInternals::GoogleSigninSucceeded(const std::string& account_id,
337 const std::string& username,
338 const std::string& password) {
339 NotifyObservers();
342 void AboutSigninInternals::GoogleSignedOut(const std::string& account_id,
343 const std::string& username) {
344 NotifyObservers();
347 void AboutSigninInternals::OnGaiaAccountsInCookieUpdated(
348 const std::vector<gaia::ListedAccount>& gaia_accounts,
349 const GoogleServiceAuthError& error) {
350 if (error.state() != GoogleServiceAuthError::NONE)
351 return;
353 base::DictionaryValue cookie_status;
354 base::ListValue* cookie_info = new base::ListValue();
355 cookie_status.Set("cookie_info", cookie_info);
357 for (size_t i = 0; i < gaia_accounts.size(); ++i) {
358 AddCookieEntry(cookie_info,
359 gaia_accounts[i].raw_email,
360 gaia_accounts[i].gaia_id,
361 gaia_accounts[i].valid ? "Valid" : "Invalid");
364 if (gaia_accounts.size() == 0) {
365 AddCookieEntry(cookie_info,
366 "No Accounts Present.",
367 std::string(),
368 std::string());
371 // Update the observers that the cookie's accounts are updated.
372 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
373 signin_observers_,
374 OnCookieAccountsFetched(&cookie_status));
377 AboutSigninInternals::TokenInfo::TokenInfo(
378 const std::string& consumer_id,
379 const OAuth2TokenService::ScopeSet& scopes)
380 : consumer_id(consumer_id),
381 scopes(scopes),
382 request_time(base::Time::Now()),
383 error(GoogleServiceAuthError::AuthErrorNone()),
384 removed_(false) {}
386 AboutSigninInternals::TokenInfo::~TokenInfo() {}
388 bool AboutSigninInternals::TokenInfo::LessThan(const TokenInfo* a,
389 const TokenInfo* b) {
390 if (a->request_time == b->request_time) {
391 if (a->consumer_id == b->consumer_id) {
392 return a->scopes < b->scopes;
394 return a->consumer_id < b->consumer_id;
396 return a->request_time < b->request_time;
399 void AboutSigninInternals::TokenInfo::Invalidate() { removed_ = true; }
401 base::DictionaryValue* AboutSigninInternals::TokenInfo::ToValue() const {
402 scoped_ptr<base::DictionaryValue> token_info(new base::DictionaryValue());
403 token_info->SetString("service", consumer_id);
405 std::string scopes_str;
406 for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
407 it != scopes.end();
408 ++it) {
409 scopes_str += *it + "<br/>";
411 token_info->SetString("scopes", scopes_str);
412 token_info->SetString("request_time", GetTimeStr(request_time).c_str());
414 if (removed_) {
415 token_info->SetString("status", "Token was revoked.");
416 } else if (!receive_time.is_null()) {
417 if (error == GoogleServiceAuthError::AuthErrorNone()) {
418 bool token_expired = expiration_time < base::Time::Now();
419 std::string status_str = "";
420 if (token_expired)
421 status_str = "<p style=\"color: #ffffff; background-color: #ff0000\">";
422 base::StringAppendF(&status_str,
423 "Received token at %s. Expire at %s",
424 GetTimeStr(receive_time).c_str(),
425 GetTimeStr(expiration_time).c_str());
426 if (token_expired)
427 base::StringAppendF(&status_str, "</p>");
428 token_info->SetString("status", status_str);
429 } else {
430 token_info->SetString(
431 "status",
432 base::StringPrintf("Failure: %s", error.ToString().c_str()));
434 } else {
435 token_info->SetString("status", "Waiting for response");
438 return token_info.release();
441 AboutSigninInternals::SigninStatus::SigninStatus()
442 : timed_signin_fields(TIMED_FIELDS_COUNT) {}
444 AboutSigninInternals::SigninStatus::~SigninStatus() {
445 for (TokenInfoMap::iterator it = token_info_map.begin();
446 it != token_info_map.end();
447 ++it) {
448 STLDeleteElements(&it->second);
452 AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken(
453 const std::string& account_id,
454 const std::string& consumer_id,
455 const OAuth2TokenService::ScopeSet& scopes) {
456 for (size_t i = 0; i < token_info_map[account_id].size(); ++i) {
457 TokenInfo* tmp = token_info_map[account_id][i];
458 if (tmp->consumer_id == consumer_id && tmp->scopes == scopes)
459 return tmp;
461 return NULL;
464 scoped_ptr<base::DictionaryValue> AboutSigninInternals::SigninStatus::ToValue(
465 AccountTrackerService* account_tracker,
466 SigninManagerBase* signin_manager,
467 SigninErrorController* signin_error_controller,
468 ProfileOAuth2TokenService* token_service,
469 const std::string& product_version) {
470 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
471 // fixed.
472 tracked_objects::ScopedTracker tracking_profile1(
473 FROM_HERE_WITH_EXPLICIT_FUNCTION(
474 "422460 AboutSigninInternals::SigninStatus::ToValue1"));
476 scoped_ptr<base::DictionaryValue> signin_status(new base::DictionaryValue());
477 base::ListValue* signin_info = new base::ListValue();
478 signin_status->Set("signin_info", signin_info);
480 // A summary of signin related info first.
481 base::ListValue* basic_info = AddSection(signin_info, "Basic Information");
482 AddSectionEntry(basic_info, "Chrome Version", product_version);
483 AddSectionEntry(basic_info, "Webview Based Signin?",
484 switches::IsEnableWebviewBasedSignin() == true ? "On" : "Off");
485 AddSectionEntry(basic_info, "New Avatar Menu?",
486 switches::IsNewAvatarMenu() == true ? "On" : "Off");
487 AddSectionEntry(basic_info, "New Profile Management?",
488 switches::IsNewProfileManagement() == true ? "On" : "Off");
489 AddSectionEntry(basic_info, "Account Consistency?",
490 switches::IsEnableAccountConsistency() == true ? "On" : "Off");
491 AddSectionEntry(basic_info, "Signin Status",
492 signin_manager->IsAuthenticated() ? "Signed In" : "Not Signed In");
494 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
495 // fixed.
496 tracked_objects::ScopedTracker tracking_profile2(
497 FROM_HERE_WITH_EXPLICIT_FUNCTION(
498 "422460 AboutSigninInternals::SigninStatus::ToValue2"));
500 if (signin_manager->IsAuthenticated()) {
501 std::string account_id = signin_manager->GetAuthenticatedAccountId();
502 AddSectionEntry(basic_info,
503 SigninStatusFieldToLabel(
504 static_cast<UntimedSigninStatusField>(ACCOUNT_ID)),
505 account_id);
506 AddSectionEntry(basic_info,
507 SigninStatusFieldToLabel(
508 static_cast<UntimedSigninStatusField>(GAIA_ID)),
509 account_tracker->GetAccountInfo(account_id).gaia);
510 AddSectionEntry(basic_info,
511 SigninStatusFieldToLabel(
512 static_cast<UntimedSigninStatusField>(USERNAME)),
513 signin_manager->GetAuthenticatedUsername());
514 if (signin_error_controller->HasError()) {
515 const std::string error_account_id =
516 signin_error_controller->error_account_id();
517 const std::string error_username =
518 account_tracker->GetAccountInfo(error_account_id).email;
519 AddSectionEntry(basic_info, "Auth Error",
520 signin_error_controller->auth_error().ToString());
521 AddSectionEntry(basic_info, "Auth Error Account Id", error_account_id);
522 AddSectionEntry(basic_info, "Auth Error Username", error_username);
523 } else {
524 AddSectionEntry(basic_info, "Auth Error", "None");
528 #if !defined(OS_CHROMEOS)
529 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
530 // fixed.
531 tracked_objects::ScopedTracker tracking_profile3(
532 FROM_HERE_WITH_EXPLICIT_FUNCTION(
533 "422460 AboutSigninInternals::SigninStatus::ToValue3"));
535 // Time and status information of the possible sign in types.
536 base::ListValue* detailed_info =
537 AddSection(signin_info, "Last Signin Details");
538 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
539 const std::string status_field_label =
540 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i));
542 AddSectionEntry(detailed_info,
543 status_field_label,
544 timed_signin_fields[i - TIMED_FIELDS_BEGIN].first,
545 timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
547 #endif // !defined(OS_CHROMEOS)
549 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
550 // fixed.
551 tracked_objects::ScopedTracker tracking_profile4(
552 FROM_HERE_WITH_EXPLICIT_FUNCTION(
553 "422460 AboutSigninInternals::SigninStatus::ToValue4"));
555 // Token information for all services.
556 base::ListValue* token_info = new base::ListValue();
557 signin_status->Set("token_info", token_info);
558 for (TokenInfoMap::iterator it = token_info_map.begin();
559 it != token_info_map.end();
560 ++it) {
561 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
562 // is fixed.
563 tracked_objects::ScopedTracker tracking_profile41(
564 FROM_HERE_WITH_EXPLICIT_FUNCTION(
565 "422460 AboutSigninInternals::SigninStatus::ToValue41"));
567 base::ListValue* token_details = AddSection(token_info, it->first);
569 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
570 // is fixed.
571 tracked_objects::ScopedTracker tracking_profile42(
572 FROM_HERE_WITH_EXPLICIT_FUNCTION(
573 "422460 AboutSigninInternals::SigninStatus::ToValue42"));
575 std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
576 const std::vector<TokenInfo*>& tokens = it->second;
578 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
579 // is fixed.
580 tracked_objects::ScopedTracker tracking_profile43(
581 FROM_HERE_WITH_EXPLICIT_FUNCTION(
582 "422460 AboutSigninInternals::SigninStatus::ToValue43"));
584 for (size_t i = 0; i < tokens.size(); ++i) {
585 base::DictionaryValue* token_info = tokens[i]->ToValue();
586 token_details->Append(token_info);
590 base::ListValue* account_info = new base::ListValue();
591 signin_status->Set("accountInfo", account_info);
592 const std::vector<std::string>& accounts_in_token_service =
593 token_service->GetAccounts();
595 if(accounts_in_token_service.size() == 0) {
596 base::DictionaryValue* no_token_entry = new base::DictionaryValue();
597 no_token_entry->SetString("accountId", "No token in Token Service.");
598 account_info->Append(no_token_entry);
601 for(const std::string& account_id : accounts_in_token_service) {
602 base::DictionaryValue* entry = new base::DictionaryValue();
603 entry->SetString("accountId", account_id);
604 account_info->Append(entry);
607 return signin_status.Pass();