Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / signin / core / browser / about_signin_internals.cc
blob1e2c98212babf5796b196ecb516b565359e777e2
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/pref_registry/pref_registry_syncable.h"
17 #include "components/signin/core/browser/account_tracker_service.h"
18 #include "components/signin/core/browser/profile_oauth2_token_service.h"
19 #include "components/signin/core/browser/signin_client.h"
20 #include "components/signin/core/browser/signin_internals_util.h"
21 #include "components/signin/core/browser/signin_manager.h"
22 #include "components/signin/core/common/profile_management_switches.h"
23 #include "components/signin/core/common/signin_switches.h"
25 using base::Time;
26 using namespace signin_internals_util;
28 namespace {
30 std::string GetTimeStr(base::Time time) {
31 return base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(time));
34 base::ListValue* AddSection(base::ListValue* parent_list,
35 const std::string& title) {
36 scoped_ptr<base::DictionaryValue> section(new base::DictionaryValue());
37 base::ListValue* section_contents = new base::ListValue();
39 section->SetString("title", title);
40 section->Set("data", section_contents);
41 parent_list->Append(section.release());
42 return section_contents;
45 void AddSectionEntry(base::ListValue* section_list,
46 const std::string& field_name,
47 const std::string& field_status,
48 const std::string& field_time = "") {
49 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
50 entry->SetString("label", field_name);
51 entry->SetString("status", field_status);
52 entry->SetString("time", field_time);
53 section_list->Append(entry.release());
56 void AddCookieEntry(base::ListValue* accounts_list,
57 const std::string& field_email,
58 const std::string& field_gaia_id,
59 const std::string& field_valid) {
60 scoped_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
61 entry->SetString("email", field_email);
62 entry->SetString("gaia_id", field_gaia_id);
63 entry->SetString("valid", field_valid);
64 accounts_list->Append(entry.release());
67 std::string SigninStatusFieldToLabel(UntimedSigninStatusField field) {
68 switch (field) {
69 case ACCOUNT_ID:
70 return "Account Id";
71 case GAIA_ID:
72 return "Gaia Id";
73 case USERNAME:
74 return "Username";
75 case UNTIMED_FIELDS_END:
76 NOTREACHED();
77 return std::string();
79 NOTREACHED();
80 return std::string();
83 #if !defined (OS_CHROMEOS)
84 std::string SigninStatusFieldToLabel(TimedSigninStatusField field) {
85 switch (field) {
86 case AUTHENTICATION_RESULT_RECEIVED:
87 return "Gaia Authentication Result";
88 case REFRESH_TOKEN_RECEIVED:
89 return "RefreshToken Received";
90 case SIGNIN_STARTED:
91 return "SigninManager Started";
92 case SIGNIN_COMPLETED:
93 return "SigninManager Completed";
94 case TIMED_FIELDS_END:
95 NOTREACHED();
96 return "Error";
98 NOTREACHED();
99 return "Error";
101 #endif // !defined (OS_CHROMEOS)
103 void SetPref(PrefService* prefs,
104 TimedSigninStatusField field,
105 const std::string& time,
106 const std::string& value) {
107 std::string value_pref = SigninStatusFieldToString(field) + ".value";
108 std::string time_pref = SigninStatusFieldToString(field) + ".time";
109 prefs->SetString(value_pref, value);
110 prefs->SetString(time_pref, time);
113 void GetPref(PrefService* prefs,
114 TimedSigninStatusField field,
115 std::string* time,
116 std::string* value) {
117 std::string value_pref = SigninStatusFieldToString(field) + ".value";
118 std::string time_pref = SigninStatusFieldToString(field) + ".time";
119 *value = prefs->GetString(value_pref);
120 *time = prefs->GetString(time_pref);
123 void ClearPref(PrefService* prefs, TimedSigninStatusField field) {
124 std::string value_pref = SigninStatusFieldToString(field) + ".value";
125 std::string time_pref = SigninStatusFieldToString(field) + ".time";
126 prefs->ClearPref(value_pref);
127 prefs->ClearPref(time_pref);
130 } // anonymous namespace
132 AboutSigninInternals::AboutSigninInternals(
133 ProfileOAuth2TokenService* token_service,
134 AccountTrackerService* account_tracker,
135 SigninManagerBase* signin_manager,
136 SigninErrorController* signin_error_controller,
137 GaiaCookieManagerService* cookie_manager_service)
138 : token_service_(token_service),
139 account_tracker_(account_tracker),
140 signin_manager_(signin_manager),
141 client_(NULL),
142 signin_error_controller_(signin_error_controller),
143 cookie_manager_service_(cookie_manager_service) {}
145 AboutSigninInternals::~AboutSigninInternals() {}
147 // static
148 void AboutSigninInternals::RegisterPrefs(
149 user_prefs::PrefRegistrySyncable* user_prefs) {
150 // SigninManager information for about:signin-internals.
152 // TODO(rogerta): leaving untimed fields here for now because legacy
153 // profiles still have these prefs. In three or four version from M43
154 // we can probably remove them.
155 for (int i = UNTIMED_FIELDS_BEGIN; i < UNTIMED_FIELDS_END; ++i) {
156 const std::string pref_path =
157 SigninStatusFieldToString(static_cast<UntimedSigninStatusField>(i));
158 user_prefs->RegisterStringPref(pref_path.c_str(), std::string());
161 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
162 const std::string value =
163 SigninStatusFieldToString(static_cast<TimedSigninStatusField>(i)) +
164 ".value";
165 const std::string time =
166 SigninStatusFieldToString(static_cast<TimedSigninStatusField>(i)) +
167 ".time";
168 user_prefs->RegisterStringPref(value.c_str(), std::string());
169 user_prefs->RegisterStringPref(time.c_str(), std::string());
173 void AboutSigninInternals::AddSigninObserver(
174 AboutSigninInternals::Observer* observer) {
175 signin_observers_.AddObserver(observer);
178 void AboutSigninInternals::RemoveSigninObserver(
179 AboutSigninInternals::Observer* observer) {
180 signin_observers_.RemoveObserver(observer);
183 void AboutSigninInternals::NotifySigninValueChanged(
184 const TimedSigninStatusField& field,
185 const std::string& value) {
186 unsigned int field_index = field - TIMED_FIELDS_BEGIN;
187 DCHECK(field_index >= 0 &&
188 field_index < signin_status_.timed_signin_fields.size());
190 Time now = Time::NowFromSystemTime();
191 std::string time_as_str =
192 base::UTF16ToUTF8(base::TimeFormatShortDateAndTime(now));
193 TimedSigninStatusValue timed_value(value, time_as_str);
195 signin_status_.timed_signin_fields[field_index] = timed_value;
197 // Also persist these values in the prefs.
198 SetPref(client_->GetPrefs(), field, value, time_as_str);
200 // If the user is restarting a sign in process, clear the fields that are
201 // to come.
202 if (field == AUTHENTICATION_RESULT_RECEIVED) {
203 ClearPref(client_->GetPrefs(), REFRESH_TOKEN_RECEIVED);
204 ClearPref(client_->GetPrefs(), SIGNIN_STARTED);
205 ClearPref(client_->GetPrefs(), SIGNIN_COMPLETED);
208 NotifyObservers();
211 void AboutSigninInternals::RefreshSigninPrefs() {
212 // Return if no client exists. Can occur in unit tests.
213 if (!client_)
214 return;
216 PrefService* pref_service = client_->GetPrefs();
217 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
218 std::string time_str;
219 std::string value_str;
220 GetPref(pref_service, static_cast<TimedSigninStatusField>(i),
221 &time_str, &value_str);
222 TimedSigninStatusValue value(value_str, time_str);
223 signin_status_.timed_signin_fields[i - TIMED_FIELDS_BEGIN] = value;
226 // TODO(rogerta): Get status and timestamps for oauth2 tokens.
228 NotifyObservers();
231 void AboutSigninInternals::Initialize(SigninClient* client) {
232 DCHECK(!client_);
233 client_ = client;
235 RefreshSigninPrefs();
237 signin_error_controller_->AddObserver(this);
238 signin_manager_->AddSigninDiagnosticsObserver(this);
239 token_service_->AddDiagnosticsObserver(this);
240 cookie_manager_service_->AddObserver(this);
243 void AboutSigninInternals::Shutdown() {
244 signin_error_controller_->RemoveObserver(this);
245 signin_manager_->RemoveSigninDiagnosticsObserver(this);
246 token_service_->RemoveDiagnosticsObserver(this);
247 cookie_manager_service_->RemoveObserver(this);
250 void AboutSigninInternals::NotifyObservers() {
251 if (!signin_observers_.might_have_observers())
252 return;
254 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
255 // fixed.
256 tracked_objects::ScopedTracker tracking_profile(
257 FROM_HERE_WITH_EXPLICIT_FUNCTION(
258 "422460 AboutSigninInternals::NotifyObservers"));
260 const std::string product_version = client_->GetProductVersion();
262 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
263 // fixed.
264 tracked_objects::ScopedTracker tracking_profile05(
265 FROM_HERE_WITH_EXPLICIT_FUNCTION(
266 "422460 AboutSigninInternals::NotifyObservers 0.5"));
268 scoped_ptr<base::DictionaryValue> signin_status_value =
269 signin_status_.ToValue(account_tracker_,
270 signin_manager_,
271 signin_error_controller_,
272 token_service_,
273 product_version);
275 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
276 // fixed.
277 tracked_objects::ScopedTracker tracking_profile1(
278 FROM_HERE_WITH_EXPLICIT_FUNCTION(
279 "422460 AboutSigninInternals::NotifyObservers1"));
281 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
282 signin_observers_,
283 OnSigninStateChanged(signin_status_value.get()));
286 scoped_ptr<base::DictionaryValue> AboutSigninInternals::GetSigninStatus() {
287 return signin_status_.ToValue(account_tracker_,
288 signin_manager_,
289 signin_error_controller_,
290 token_service_,
291 client_->GetProductVersion()).Pass();
294 void AboutSigninInternals::OnAccessTokenRequested(
295 const std::string& account_id,
296 const std::string& consumer_id,
297 const OAuth2TokenService::ScopeSet& scopes) {
298 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
299 // fixed.
300 tracked_objects::ScopedTracker tracking_profile(
301 FROM_HERE_WITH_EXPLICIT_FUNCTION(
302 "422460 AboutSigninInternals::OnAccessTokenRequested"));
304 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
305 if (token) {
306 *token = TokenInfo(consumer_id, scopes);
307 } else {
308 token = new TokenInfo(consumer_id, scopes);
309 signin_status_.token_info_map[account_id].push_back(token);
312 NotifyObservers();
315 void AboutSigninInternals::OnFetchAccessTokenComplete(
316 const std::string& account_id,
317 const std::string& consumer_id,
318 const OAuth2TokenService::ScopeSet& scopes,
319 GoogleServiceAuthError error,
320 base::Time expiration_time) {
321 TokenInfo* token = signin_status_.FindToken(account_id, consumer_id, scopes);
322 if (!token) {
323 DVLOG(1) << "Can't find token: " << account_id << ", " << consumer_id;
324 return;
327 token->receive_time = base::Time::Now();
328 token->error = error;
329 token->expiration_time = expiration_time;
331 NotifyObservers();
334 void AboutSigninInternals::OnTokenRemoved(
335 const std::string& account_id,
336 const OAuth2TokenService::ScopeSet& scopes) {
337 for (size_t i = 0; i < signin_status_.token_info_map[account_id].size();
338 ++i) {
339 TokenInfo* token = signin_status_.token_info_map[account_id][i];
340 if (token->scopes == scopes)
341 token->Invalidate();
343 NotifyObservers();
346 void AboutSigninInternals::OnRefreshTokenReceived(std::string status) {
347 NotifySigninValueChanged(REFRESH_TOKEN_RECEIVED, status);
350 void AboutSigninInternals::OnAuthenticationResultReceived(std::string status) {
351 NotifySigninValueChanged(AUTHENTICATION_RESULT_RECEIVED, status);
354 void AboutSigninInternals::OnErrorChanged() {
355 NotifyObservers();
358 void AboutSigninInternals::GoogleSigninFailed(
359 const GoogleServiceAuthError& error) {
360 NotifyObservers();
363 void AboutSigninInternals::GoogleSigninSucceeded(const std::string& account_id,
364 const std::string& username,
365 const std::string& password) {
366 NotifyObservers();
369 void AboutSigninInternals::GoogleSignedOut(const std::string& account_id,
370 const std::string& username) {
371 NotifyObservers();
374 void AboutSigninInternals::OnGaiaAccountsInCookieUpdated(
375 const std::vector<gaia::ListedAccount>& gaia_accounts,
376 const GoogleServiceAuthError& error) {
377 if (error.state() != GoogleServiceAuthError::NONE)
378 return;
380 base::DictionaryValue cookie_status;
381 base::ListValue* cookie_info = new base::ListValue();
382 cookie_status.Set("cookie_info", cookie_info);
384 for (size_t i = 0; i < gaia_accounts.size(); ++i) {
385 AddCookieEntry(cookie_info,
386 gaia_accounts[i].raw_email,
387 gaia_accounts[i].gaia_id,
388 gaia_accounts[i].valid ? "Valid" : "Invalid");
391 if (gaia_accounts.size() == 0) {
392 AddCookieEntry(cookie_info,
393 "No Accounts Present.",
394 std::string(),
395 std::string());
398 // Update the observers that the cookie's accounts are updated.
399 FOR_EACH_OBSERVER(AboutSigninInternals::Observer,
400 signin_observers_,
401 OnCookieAccountsFetched(&cookie_status));
404 AboutSigninInternals::TokenInfo::TokenInfo(
405 const std::string& consumer_id,
406 const OAuth2TokenService::ScopeSet& scopes)
407 : consumer_id(consumer_id),
408 scopes(scopes),
409 request_time(base::Time::Now()),
410 error(GoogleServiceAuthError::AuthErrorNone()),
411 removed_(false) {}
413 AboutSigninInternals::TokenInfo::~TokenInfo() {}
415 bool AboutSigninInternals::TokenInfo::LessThan(const TokenInfo* a,
416 const TokenInfo* b) {
417 if (a->request_time == b->request_time) {
418 if (a->consumer_id == b->consumer_id) {
419 return a->scopes < b->scopes;
421 return a->consumer_id < b->consumer_id;
423 return a->request_time < b->request_time;
426 void AboutSigninInternals::TokenInfo::Invalidate() { removed_ = true; }
428 base::DictionaryValue* AboutSigninInternals::TokenInfo::ToValue() const {
429 scoped_ptr<base::DictionaryValue> token_info(new base::DictionaryValue());
430 token_info->SetString("service", consumer_id);
432 std::string scopes_str;
433 for (OAuth2TokenService::ScopeSet::const_iterator it = scopes.begin();
434 it != scopes.end();
435 ++it) {
436 scopes_str += *it + "<br/>";
438 token_info->SetString("scopes", scopes_str);
439 token_info->SetString("request_time", GetTimeStr(request_time).c_str());
441 if (removed_) {
442 token_info->SetString("status", "Token was revoked.");
443 } else if (!receive_time.is_null()) {
444 if (error == GoogleServiceAuthError::AuthErrorNone()) {
445 bool token_expired = expiration_time < base::Time::Now();
446 std::string expiration_time_string = GetTimeStr(expiration_time);
447 if (expiration_time.is_null()) {
448 token_expired = false;
449 expiration_time_string = "Expiration time not available";
451 std::string status_str = "";
452 if (token_expired)
453 status_str = "<p style=\"color: #ffffff; background-color: #ff0000\">";
454 base::StringAppendF(&status_str, "Received token at %s. Expire at %s",
455 GetTimeStr(receive_time).c_str(),
456 expiration_time_string.c_str());
457 if (token_expired)
458 base::StringAppendF(&status_str, "</p>");
459 token_info->SetString("status", status_str);
460 } else {
461 token_info->SetString(
462 "status",
463 base::StringPrintf("Failure: %s", error.ToString().c_str()));
465 } else {
466 token_info->SetString("status", "Waiting for response");
469 return token_info.release();
472 AboutSigninInternals::SigninStatus::SigninStatus()
473 : timed_signin_fields(TIMED_FIELDS_COUNT) {}
475 AboutSigninInternals::SigninStatus::~SigninStatus() {
476 for (TokenInfoMap::iterator it = token_info_map.begin();
477 it != token_info_map.end();
478 ++it) {
479 STLDeleteElements(&it->second);
483 AboutSigninInternals::TokenInfo* AboutSigninInternals::SigninStatus::FindToken(
484 const std::string& account_id,
485 const std::string& consumer_id,
486 const OAuth2TokenService::ScopeSet& scopes) {
487 for (size_t i = 0; i < token_info_map[account_id].size(); ++i) {
488 TokenInfo* tmp = token_info_map[account_id][i];
489 if (tmp->consumer_id == consumer_id && tmp->scopes == scopes)
490 return tmp;
492 return NULL;
495 scoped_ptr<base::DictionaryValue> AboutSigninInternals::SigninStatus::ToValue(
496 AccountTrackerService* account_tracker,
497 SigninManagerBase* signin_manager,
498 SigninErrorController* signin_error_controller,
499 ProfileOAuth2TokenService* token_service,
500 const std::string& product_version) {
501 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
502 // fixed.
503 tracked_objects::ScopedTracker tracking_profile1(
504 FROM_HERE_WITH_EXPLICIT_FUNCTION(
505 "422460 AboutSigninInternals::SigninStatus::ToValue1"));
507 scoped_ptr<base::DictionaryValue> signin_status(new base::DictionaryValue());
508 base::ListValue* signin_info = new base::ListValue();
509 signin_status->Set("signin_info", signin_info);
511 // A summary of signin related info first.
512 base::ListValue* basic_info = AddSection(signin_info, "Basic Information");
513 AddSectionEntry(basic_info, "Chrome Version", product_version);
514 AddSectionEntry(basic_info, "Webview Based Signin?",
515 switches::IsEnableWebviewBasedSignin() == true ? "On" : "Off");
516 AddSectionEntry(basic_info, "New Avatar Menu?",
517 switches::IsNewAvatarMenu() == true ? "On" : "Off");
518 AddSectionEntry(basic_info, "New Profile Management?",
519 switches::IsNewProfileManagement() == true ? "On" : "Off");
520 AddSectionEntry(basic_info, "Account Consistency?",
521 switches::IsEnableAccountConsistency() == true ? "On" : "Off");
522 AddSectionEntry(basic_info, "Signin Status",
523 signin_manager->IsAuthenticated() ? "Signed In" : "Not Signed In");
525 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
526 // fixed.
527 tracked_objects::ScopedTracker tracking_profile2(
528 FROM_HERE_WITH_EXPLICIT_FUNCTION(
529 "422460 AboutSigninInternals::SigninStatus::ToValue2"));
531 if (signin_manager->IsAuthenticated()) {
532 std::string account_id = signin_manager->GetAuthenticatedAccountId();
533 AddSectionEntry(basic_info,
534 SigninStatusFieldToLabel(
535 static_cast<UntimedSigninStatusField>(ACCOUNT_ID)),
536 account_id);
537 AddSectionEntry(basic_info,
538 SigninStatusFieldToLabel(
539 static_cast<UntimedSigninStatusField>(GAIA_ID)),
540 account_tracker->GetAccountInfo(account_id).gaia);
541 AddSectionEntry(basic_info,
542 SigninStatusFieldToLabel(
543 static_cast<UntimedSigninStatusField>(USERNAME)),
544 signin_manager->GetAuthenticatedAccountInfo().email);
545 if (signin_error_controller->HasError()) {
546 const std::string error_account_id =
547 signin_error_controller->error_account_id();
548 const std::string error_username =
549 account_tracker->GetAccountInfo(error_account_id).email;
550 AddSectionEntry(basic_info, "Auth Error",
551 signin_error_controller->auth_error().ToString());
552 AddSectionEntry(basic_info, "Auth Error Account Id", error_account_id);
553 AddSectionEntry(basic_info, "Auth Error Username", error_username);
554 } else {
555 AddSectionEntry(basic_info, "Auth Error", "None");
559 #if !defined(OS_CHROMEOS)
560 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
561 // fixed.
562 tracked_objects::ScopedTracker tracking_profile3(
563 FROM_HERE_WITH_EXPLICIT_FUNCTION(
564 "422460 AboutSigninInternals::SigninStatus::ToValue3"));
566 // Time and status information of the possible sign in types.
567 base::ListValue* detailed_info =
568 AddSection(signin_info, "Last Signin Details");
569 for (int i = TIMED_FIELDS_BEGIN; i < TIMED_FIELDS_END; ++i) {
570 const std::string status_field_label =
571 SigninStatusFieldToLabel(static_cast<TimedSigninStatusField>(i));
573 AddSectionEntry(detailed_info,
574 status_field_label,
575 timed_signin_fields[i - TIMED_FIELDS_BEGIN].first,
576 timed_signin_fields[i - TIMED_FIELDS_BEGIN].second);
578 #endif // !defined(OS_CHROMEOS)
580 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460 is
581 // fixed.
582 tracked_objects::ScopedTracker tracking_profile4(
583 FROM_HERE_WITH_EXPLICIT_FUNCTION(
584 "422460 AboutSigninInternals::SigninStatus::ToValue4"));
586 // Token information for all services.
587 base::ListValue* token_info = new base::ListValue();
588 signin_status->Set("token_info", token_info);
589 for (TokenInfoMap::iterator it = token_info_map.begin();
590 it != token_info_map.end();
591 ++it) {
592 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
593 // is fixed.
594 tracked_objects::ScopedTracker tracking_profile41(
595 FROM_HERE_WITH_EXPLICIT_FUNCTION(
596 "422460 AboutSigninInternals::SigninStatus::ToValue41"));
598 base::ListValue* token_details = AddSection(token_info, it->first);
600 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
601 // is fixed.
602 tracked_objects::ScopedTracker tracking_profile42(
603 FROM_HERE_WITH_EXPLICIT_FUNCTION(
604 "422460 AboutSigninInternals::SigninStatus::ToValue42"));
606 std::sort(it->second.begin(), it->second.end(), TokenInfo::LessThan);
607 const std::vector<TokenInfo*>& tokens = it->second;
609 // TODO(robliao): Remove ScopedTracker below once https://crbug.com/422460
610 // is fixed.
611 tracked_objects::ScopedTracker tracking_profile43(
612 FROM_HERE_WITH_EXPLICIT_FUNCTION(
613 "422460 AboutSigninInternals::SigninStatus::ToValue43"));
615 for (size_t i = 0; i < tokens.size(); ++i) {
616 base::DictionaryValue* token_info = tokens[i]->ToValue();
617 token_details->Append(token_info);
621 base::ListValue* account_info = new base::ListValue();
622 signin_status->Set("accountInfo", account_info);
623 const std::vector<std::string>& accounts_in_token_service =
624 token_service->GetAccounts();
626 if(accounts_in_token_service.size() == 0) {
627 base::DictionaryValue* no_token_entry = new base::DictionaryValue();
628 no_token_entry->SetString("accountId", "No token in Token Service.");
629 account_info->Append(no_token_entry);
632 for(const std::string& account_id : accounts_in_token_service) {
633 base::DictionaryValue* entry = new base::DictionaryValue();
634 entry->SetString("accountId", account_id);
635 account_info->Append(entry);
638 return signin_status.Pass();