Port Android relocation packer to chromium build
[chromium-blink-merge.git] / components / signin / core / browser / about_signin_internals.cc
bloba693d5e8004d0fe75ea7daf6f71c7ad3b09e61ad
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"
23 #include "google_apis/gaia/gaia_auth_fetcher.h"
24 #include "google_apis/gaia/gaia_auth_util.h"
25 #include "google_apis/gaia/gaia_constants.h"
26 #include "google_apis/gaia/gaia_urls.h"
27 #include "net/cookies/canonical_cookie.h"
29 using base::Time;
30 using namespace signin_internals_util;
32 namespace {
34 std::string GetTimeStr(base::Time time) {
35 return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time));
38 base::ListValue* AddSection(base::ListValue* parent_list,
39 const std::string& title) {
40 scoped_ptr<base::DictionaryValue> section(new base::DictionaryValue());
41 base::ListValue* section_contents = new base::ListValue();
43 section->SetString("title", title);
44 section->Set("data", section_contents);
45 parent_list->Append(section.release());
46 return section_contents;
49 void AddSectionEntry(base::ListValue* section_list,
50 const std::string& field_name,
51 const std::string& field_status,
52 const std::string& field_time = "") {
53 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
54 entry->SetString("label", field_name);
55 entry->SetString("status", field_status);
56 entry->SetString("time", field_time);
57 section_list->Append(entry.release());
60 void AddCookieEntry(base::ListValue* accounts_list,
61 const std::string& field_email,
62 const std::string& field_valid) {
63 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
64 entry->SetString("email", field_email);
65 entry->SetString("valid", field_valid);
66 accounts_list->Append(entry.release());
69 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
70 switch (field) {
71 case ACCOUNT_ID:
72 return "Account Id";
73 case GAIA_ID:
74 return "Gaia Id";
75 case USERNAME:
76 return "Username";
77 case UNTIMED_FIELDS_END:
78 NOTREACHED();
79 return std::string();
81 NOTREACHED();
82 return std::string();
85 #if !defined (OS_CHROMEOS)
86 std::string SigninStatusFieldToLabel(TimedSigninStatusField field) {
87 switch (field) {
88 case AUTHENTICATION_RESULT_RECEIVED:
89 return "Gaia Authentication Result";
90 case REFRESH_TOKEN_RECEIVED:
91 return "RefreshToken Received";
92 case SIGNIN_STARTED:
93 return "SigninManager Started";
94 case SIGNIN_COMPLETED:
95 return "SigninManager Completed";
96 case TIMED_FIELDS_END:
97 NOTREACHED();
98 return "Error";
100 NOTREACHED();
101 return "Error";
103 #endif // !defined (OS_CHROMEOS)
105 void SetPref(PrefService* prefs,
106 TimedSigninStatusField field,
107 const std::string& time,
108 const std::string& value) {
109 std::string value_pref = SigninStatusFieldToString(field) + ".value";
110 std::string time_pref = SigninStatusFieldToString(field) + ".time";
111 prefs->SetString(value_pref, value);
112 prefs->SetString(time_pref, time);
115 void GetPref(PrefService* prefs,
116 TimedSigninStatusField field,
117 std::string* time,
118 std::string* value) {
119 std::string value_pref = SigninStatusFieldToString(field) + ".value";
120 std::string time_pref = SigninStatusFieldToString(field) + ".time";
121 *value = prefs->GetString(value_pref);
122 *time = prefs->GetString(time_pref);
125 void ClearPref(PrefService* prefs, TimedSigninStatusField field) {
126 std::string value_pref = SigninStatusFieldToString(field) + ".value";
127 std::string time_pref = SigninStatusFieldToString(field) + ".time";
128 prefs->ClearPref(value_pref);
129 prefs->ClearPref(time_pref);
132 } // anonymous namespace
134 AboutSigninInternals::AboutSigninInternals(
135 ProfileOAuth2TokenService* token_service,
136 AccountTrackerService* account_tracker,
137 SigninManagerBase* signin_manager)
138 : token_service_(token_service),
139 account_tracker_(account_tracker),
140 signin_manager_(signin_manager),
141 client_(NULL) {}
143 AboutSigninInternals::~AboutSigninInternals() {}
145 void AboutSigninInternals::AddSigninObserver(
146 AboutSigninInternals::Observer* observer) {
147 signin_observers_.AddObserver(observer);
150 void AboutSigninInternals::RemoveSigninObserver(
151 AboutSigninInternals::Observer* observer) {
152 signin_observers_.RemoveObserver(observer);
155 void AboutSigninInternals::NotifySigninValueChanged(
156 const TimedSigninStatusField& field,
157 const std::string& value) {
158 unsigned int field_index = field - TIMED_FIELDS_BEGIN;
159 DCHECK(field_index >= 0 &&
160 field_index < signin_status_.timed_signin_fields.size());
162 Time now = Time::NowFromSystemTime();
163 std::string time_as_str =
164 base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(now));
165 TimedSigninStatusValue timed_value(value, time_as_str);
167 signin_status_.timed_signin_fields[field_index] = timed_value;
169 // Also persist these values in the prefs.
170 SetPref(client_->GetPrefs(), field, value, time_as_str);
172 // If the user is restarting a sign in process, clear the fields that are
173 // to come.
174 if (field == AUTHENTICATION_RESULT_RECEIVED) {
175 ClearPref(client_->GetPrefs(), REFRESH_TOKEN_RECEIVED);
176 ClearPref(client_->GetPrefs(), SIGNIN_STARTED);
177 ClearPref(client_->GetPrefs(), SIGNIN_COMPLETED);
180 NotifyObservers();
183 void AboutSigninInternals::RefreshSigninPrefs() {
184 // Return if no client exists. Can occur in unit tests.
185 if (!client_)
186 return;
188 PrefService* pref_service = client_->GetPrefs();
189 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
190 std::string time_str;
191 std::string value_str;
192 GetPref(pref_service, static_cast<TimedSigninStatusField>(i),
193 &time_str, &value_str);
194 TimedSigninStatusValue value(value_str, time_str);
195 signin_status_.timed_signin_fields[i - TIMED_FIELDS_BEGIN] = value;
198 // TODO(rogerta): Get status and timestamps for oauth2 tokens.
200 NotifyObservers();
203 void AboutSigninInternals::Initialize(SigninClient* client) {
204 DCHECK(!client_);
205 client_ = client;
207 RefreshSigninPrefs();
209 signin_manager_->AddSigninDiagnosticsObserver(this);
210 token_service_->AddDiagnosticsObserver(this);
211 cookie_changed_subscription_ = client_->AddCookieChangedCallback(
212 GaiaUrls::GetInstance()->gaia_url(),
213 "LSID",
214 base::Bind(&AboutSigninInternals::OnCookieChanged,
215 base::Unretained(this)));
218 void AboutSigninInternals::Shutdown() {
219 signin_manager_->RemoveSigninDiagnosticsObserver(this);
220 token_service_->RemoveDiagnosticsObserver(this);
221 cookie_changed_subscription_.reset();
224 void AboutSigninInternals::NotifyObservers() {
225 if (!signin_observers_.might_have_observers())
226 return;
228 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
229 // fixed.
230 tracked_objects::ScopedTracker tracking_profile(
231 FROM_HERE_WITH_EXPLICIT_FUNCTION(
232 "422460 AboutSigninInternals::NotifyObservers"));
234 const std::string product_version = client_->GetProductVersion();
236 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
237 // fixed.
238 tracked_objects::ScopedTracker tracking_profile05(
239 FROM_HERE_WITH_EXPLICIT_FUNCTION(
240 "422460 AboutSigninInternals::NotifyObservers 0.5"));
242 scoped_ptr<base::DictionaryValue> signin_status_value =
243 signin_status_.ToValue(account_tracker_, signin_manager_,
244 product_version);
246 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
247 // fixed.
248 tracked_objects::ScopedTracker tracking_profile1(
249 FROM_HERE_WITH_EXPLICIT_FUNCTION(
250 "422460 AboutSigninInternals::NotifyObservers1"));
252 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
253 signin_observers_,
254 OnSigninStateChanged(signin_status_value.get()));
257 scoped_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
258 return signin_status_.ToValue(account_tracker_, signin_manager_,
259 client_->GetProductVersion()).Pass();
262 void AboutSigninInternals::OnAccessTokenRequested(
263 const std::string& account_id,
264 const std::string& consumer_id,
265 const OAuth2TokenService::ScopeSet& scopes) {
266 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
267 // fixed.
268 tracked_objects::ScopedTracker tracking_profile(
269 FROM_HERE_WITH_EXPLICIT_FUNCTION(
270 "422460 AboutSigninInternals::OnAccessTokenRequested"));
272 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
273 if (token) {
274 *token = TokenInfo(consumer_id, scopes);
275 } else {
276 token = new TokenInfo(consumer_id, scopes);
277 signin_status_.token_info_map[account_id].push_back(token);
280 NotifyObservers();
283 void AboutSigninInternals::OnFetchAccessTokenComplete(
284 const std::string& account_id,
285 const std::string& consumer_id,
286 const OAuth2TokenService::ScopeSet& scopes,
287 GoogleServiceAuthError error,
288 base::Time expiration_time) {
289 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
290 if (!token) {
291 DVLOG(1) << "Can't find token: " << account_id << ", " << consumer_id;
292 return;
295 token->receive_time = base::Time::Now();
296 token->error = error;
297 token->expiration_time = expiration_time;
299 NotifyObservers();
302 void AboutSigninInternals::OnTokenRemoved(
303 const std::string& account_id,
304 const OAuth2TokenService::ScopeSet& scopes) {
305 for (size_t i = 0; i < signin_status_.token_info_map[account_id].size();
306 ++i) {
307 TokenInfo* token = signin_status_.token_info_map[account_id][i];
308 if (token->scopes == scopes)
309 token->Invalidate();
311 NotifyObservers();
314 void AboutSigninInternals::OnRefreshTokenReceived(std::string status) {
315 NotifySigninValueChanged(REFRESH_TOKEN_RECEIVED, status);
318 void AboutSigninInternals::OnAuthenticationResultReceived(std::string status) {
319 NotifySigninValueChanged(AUTHENTICATION_RESULT_RECEIVED, status);
322 void AboutSigninInternals::OnCookieChanged(const net::CanonicalCookie& cookie,
323 bool removed) {
324 DCHECK_EQ("LSID", cookie.Name());
325 DCHECK_EQ(GaiaUrls::GetInstance()->gaia_url().host(), cookie.Domain());
326 if (cookie.IsSecure() && cookie.IsHttpOnly()) {
327 GetCookieAccountsAsync();
331 void AboutSigninInternals::GetCookieAccountsAsync() {
332 // Don't bother calling /ListAccounts if no one will observe the response.
333 if (!gaia_fetcher_ && signin_observers_.might_have_observers()) {
334 // There is no list account request in flight.
335 gaia_fetcher_.reset(new GaiaAuthFetcher(
336 this, GaiaConstants::kChromeSource, client_->GetURLRequestContext()));
337 gaia_fetcher_->StartListAccounts();
341 void AboutSigninInternals::OnListAccountsSuccess(const std::string& data) {
342 gaia_fetcher_.reset();
344 // Get account information from response data.
345 std::vector<std::pair<std::string, bool> > gaia_accounts;
346 bool valid_json = gaia::ParseListAccountsData(data, &gaia_accounts);
347 if (!valid_json) {
348 VLOG(1) << "AboutSigninInternals::OnListAccountsSuccess: parsing error";
349 } else {
350 OnListAccountsComplete(gaia_accounts);
354 void AboutSigninInternals::OnListAccountsFailure(
355 const GoogleServiceAuthError& error) {
356 gaia_fetcher_.reset();
357 VLOG(1) << "AboutSigninInternals::OnListAccountsFailure:" << error.ToString();
360 void AboutSigninInternals::GoogleSigninFailed(
361 const GoogleServiceAuthError& error) {
362 NotifyObservers();
365 void AboutSigninInternals::GoogleSigninSucceeded(const std::string& account_id,
366 const std::string& username,
367 const std::string& password) {
368 NotifyObservers();
371 void AboutSigninInternals::GoogleSignedOut(const std::string& account_id,
372 const std::string& username) {
373 NotifyObservers();
376 void AboutSigninInternals::OnListAccountsComplete(
377 std::vector<std::pair<std::string, bool> >& gaia_accounts) {
378 base::DictionaryValue cookie_status;
379 base::ListValue* cookie_info = new base::ListValue();
380 cookie_status.Set("cookie_info", cookie_info);
382 for (size_t i = 0; i < gaia_accounts.size(); ++i) {
383 AddCookieEntry(cookie_info,
384 gaia_accounts[i].first,
385 gaia_accounts[i].second ? "Valid" : "Invalid");
388 if (gaia_accounts.size() == 0)
389 AddCookieEntry(cookie_info, "No Accounts Present.", "");
391 // Update the observers that the cookie's accounts are updated.
392 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
393 signin_observers_,
394 OnCookieAccountsFetched(&cookie_status));
397 AboutSigninInternals::TokenInfo::TokenInfo(
398 const std::string& consumer_id,
399 const OAuth2TokenService::ScopeSet& scopes)
400 : consumer_id(consumer_id),
401 scopes(scopes),
402 request_time(base::Time::Now()),
403 error(GoogleServiceAuthError::AuthErrorNone()),
404 removed_(false) {}
406 AboutSigninInternals::TokenInfo::~TokenInfo() {}
408 bool AboutSigninInternals::TokenInfo::LessThan(const TokenInfo* a,
409 const TokenInfo* b) {
410 return a->consumer_id < b->consumer_id ||
411 (a->consumer_id == b->consumer_id && a->scopes < b->scopes);
414 void AboutSigninInternals::TokenInfo::Invalidate() { removed_ = true; }
416 base::DictionaryValue* AboutSigninInternals::TokenInfo::ToValue() const {
417 scoped_ptr<base::DictionaryValue> token_info(new base::DictionaryValue());
418 token_info->SetString("service", consumer_id);
420 std::string scopes_str;
421 for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
422 it != scopes.end();
423 ++it) {
424 scopes_str += *it + "<br/>";
426 token_info->SetString("scopes", scopes_str);
427 token_info->SetString("request_time", GetTimeStr(request_time).c_str());
429 if (removed_) {
430 token_info->SetString("status", "Token was revoked.");
431 } else if (!receive_time.is_null()) {
432 if (error == GoogleServiceAuthError::AuthErrorNone()) {
433 bool token_expired = expiration_time < base::Time::Now();
434 std::string status_str = "";
435 if (token_expired)
436 status_str = "<p style=\"color: #ffffff; background-color: #ff0000\">";
437 base::StringAppendF(&status_str,
438 "Received token at %s. Expire at %s",
439 GetTimeStr(receive_time).c_str(),
440 GetTimeStr(expiration_time).c_str());
441 if (token_expired)
442 base::StringAppendF(&status_str, "</p>");
443 token_info->SetString("status", status_str);
444 } else {
445 token_info->SetString(
446 "status",
447 base::StringPrintf("Failure: %s", error.ToString().c_str()));
449 } else {
450 token_info->SetString("status", "Waiting for response");
453 return token_info.release();
456 AboutSigninInternals::SigninStatus::SigninStatus()
457 : timed_signin_fields(TIMED_FIELDS_COUNT) {}
459 AboutSigninInternals::SigninStatus::~SigninStatus() {
460 for (TokenInfoMap::iterator it = token_info_map.begin();
461 it != token_info_map.end();
462 ++it) {
463 STLDeleteElements(&it->second);
467 AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken(
468 const std::string& account_id,
469 const std::string& consumer_id,
470 const OAuth2TokenService::ScopeSet& scopes) {
471 for (size_t i = 0; i < token_info_map[account_id].size(); ++i) {
472 TokenInfo* tmp = token_info_map[account_id][i];
473 if (tmp->consumer_id == consumer_id && tmp->scopes == scopes)
474 return tmp;
476 return NULL;
479 scoped_ptr<base::DictionaryValue> AboutSigninInternals::SigninStatus::ToValue(
480 AccountTrackerService* account_tracker,
481 SigninManagerBase* signin_manager,
482 const std::string& product_version) {
483 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
484 // fixed.
485 tracked_objects::ScopedTracker tracking_profile1(
486 FROM_HERE_WITH_EXPLICIT_FUNCTION(
487 "422460 AboutSigninInternals::SigninStatus::ToValue1"));
489 scoped_ptr<base::DictionaryValue> signin_status(new base::DictionaryValue());
490 base::ListValue* signin_info = new base::ListValue();
491 signin_status->Set("signin_info", signin_info);
493 // A summary of signin related info first.
494 base::ListValue* basic_info = AddSection(signin_info, "Basic Information");
495 AddSectionEntry(basic_info, "Chrome Version", product_version);
496 AddSectionEntry(basic_info, "Webview Based Signin?",
497 switches::IsEnableWebviewBasedSignin() == true ? "On" : "Off");
498 AddSectionEntry(basic_info, "New Avatar Menu?",
499 switches::IsNewAvatarMenu() == true ? "On" : "Off");
500 AddSectionEntry(basic_info, "New Profile Management?",
501 switches::IsNewProfileManagement() == true ? "On" : "Off");
502 AddSectionEntry(basic_info, "Account Consistency?",
503 switches::IsEnableAccountConsistency() == true ? "On" : "Off");
504 AddSectionEntry(basic_info, "Signin Status",
505 signin_manager->IsAuthenticated() ? "Signed In" : "Not Signed In");
507 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
508 // fixed.
509 tracked_objects::ScopedTracker tracking_profile2(
510 FROM_HERE_WITH_EXPLICIT_FUNCTION(
511 "422460 AboutSigninInternals::SigninStatus::ToValue2"));
513 if (signin_manager->IsAuthenticated()) {
514 std::string account_id = signin_manager->GetAuthenticatedAccountId();
515 AddSectionEntry(basic_info,
516 SigninStatusFieldToLabel(
517 static_cast<UntimedSigninStatusField>(ACCOUNT_ID)),
518 account_id);
519 AddSectionEntry(basic_info,
520 SigninStatusFieldToLabel(
521 static_cast<UntimedSigninStatusField>(GAIA_ID)),
522 account_tracker->GetAccountInfo(account_id).gaia);
523 AddSectionEntry(basic_info,
524 SigninStatusFieldToLabel(
525 static_cast<UntimedSigninStatusField>(USERNAME)),
526 signin_manager->GetAuthenticatedUsername());
529 #if !defined(OS_CHROMEOS)
530 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
531 // fixed.
532 tracked_objects::ScopedTracker tracking_profile3(
533 FROM_HERE_WITH_EXPLICIT_FUNCTION(
534 "422460 AboutSigninInternals::SigninStatus::ToValue3"));
536 // Time and status information of the possible sign in types.
537 base::ListValue* detailed_info =
538 AddSection(signin_info, "Last Signin Details");
539 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
540 const std::string status_field_label =
541 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i));
543 AddSectionEntry(detailed_info,
544 status_field_label,
545 timed_signin_fields[i - TIMED_FIELDS_BEGIN].first,
546 timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
548 #endif // !defined(OS_CHROMEOS)
550 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
551 // fixed.
552 tracked_objects::ScopedTracker tracking_profile4(
553 FROM_HERE_WITH_EXPLICIT_FUNCTION(
554 "422460 AboutSigninInternals::SigninStatus::ToValue4"));
556 // Token information for all services.
557 base::ListValue* token_info = new base::ListValue();
558 signin_status->Set("token_info", token_info);
559 for (TokenInfoMap::iterator it = token_info_map.begin();
560 it != token_info_map.end();
561 ++it) {
562 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
563 // is fixed.
564 tracked_objects::ScopedTracker tracking_profile41(
565 FROM_HERE_WITH_EXPLICIT_FUNCTION(
566 "422460 AboutSigninInternals::SigninStatus::ToValue41"));
568 base::ListValue* token_details = AddSection(token_info, it->first);
570 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
571 // is fixed.
572 tracked_objects::ScopedTracker tracking_profile42(
573 FROM_HERE_WITH_EXPLICIT_FUNCTION(
574 "422460 AboutSigninInternals::SigninStatus::ToValue42"));
576 std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
577 const std::vector<TokenInfo*>& tokens = it->second;
579 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
580 // is fixed.
581 tracked_objects::ScopedTracker tracking_profile43(
582 FROM_HERE_WITH_EXPLICIT_FUNCTION(
583 "422460 AboutSigninInternals::SigninStatus::ToValue43"));
585 for (size_t i = 0; i < tokens.size(); ++i) {
586 base::DictionaryValue* token_info = tokens[i]->ToValue();
587 token_details->Append(token_info);
591 return signin_status.Pass();