existing_user_controller.cc revision 116680a4aac90f2aa7413d9095a592090648e557
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.
4
5#include "chrome/browser/chromeos/login/existing_user_controller.h"
6
7#include <vector>
8
9#include "base/bind.h"
10#include "base/bind_helpers.h"
11#include "base/callback.h"
12#include "base/command_line.h"
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/message_loop/message_loop.h"
16#include "base/metrics/histogram.h"
17#include "base/prefs/pref_service.h"
18#include "base/strings/string_util.h"
19#include "base/strings/stringprintf.h"
20#include "base/strings/utf_string_conversions.h"
21#include "base/values.h"
22#include "base/version.h"
23#include "chrome/browser/accessibility/accessibility_events.h"
24#include "chrome/browser/browser_process.h"
25#include "chrome/browser/chrome_notification_types.h"
26#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
27#include "chrome/browser/chromeos/boot_times_loader.h"
28#include "chrome/browser/chromeos/customization_document.h"
29#include "chrome/browser/chromeos/first_run/first_run.h"
30#include "chrome/browser/chromeos/kiosk_mode/kiosk_mode_settings.h"
31#include "chrome/browser/chromeos/login/helper.h"
32#include "chrome/browser/chromeos/login/login_utils.h"
33#include "chrome/browser/chromeos/login/startup_utils.h"
34#include "chrome/browser/chromeos/login/ui/login_display_host.h"
35#include "chrome/browser/chromeos/login/users/user_manager.h"
36#include "chrome/browser/chromeos/login/wizard_controller.h"
37#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
38#include "chrome/browser/chromeos/policy/device_local_account.h"
39#include "chrome/browser/chromeos/profiles/profile_helper.h"
40#include "chrome/browser/chromeos/settings/cros_settings.h"
41#include "chrome/browser/prefs/session_startup_pref.h"
42#include "chrome/common/chrome_switches.h"
43#include "chrome/common/chrome_version_info.h"
44#include "chrome/common/pref_names.h"
45#include "chrome/common/url_constants.h"
46#include "chromeos/chromeos_switches.h"
47#include "chromeos/dbus/dbus_thread_manager.h"
48#include "chromeos/dbus/power_manager_client.h"
49#include "chromeos/dbus/session_manager_client.h"
50#include "chromeos/login/auth/user_context.h"
51#include "chromeos/login/user_names.h"
52#include "chromeos/settings/cros_settings_names.h"
53#include "components/google/core/browser/google_util.h"
54#include "components/policy/core/common/policy_service.h"
55#include "components/user_manager/user_type.h"
56#include "content/public/browser/browser_thread.h"
57#include "content/public/browser/notification_service.h"
58#include "content/public/browser/notification_types.h"
59#include "content/public/browser/user_metrics.h"
60#include "google_apis/gaia/gaia_auth_util.h"
61#include "google_apis/gaia/google_service_auth_error.h"
62#include "grit/generated_resources.h"
63#include "net/http/http_auth_cache.h"
64#include "net/http/http_network_session.h"
65#include "net/http/http_transaction_factory.h"
66#include "net/url_request/url_request_context.h"
67#include "net/url_request/url_request_context_getter.h"
68#include "ui/accessibility/ax_enums.h"
69#include "ui/base/l10n/l10n_util.h"
70#include "ui/views/widget/widget.h"
71
72namespace chromeos {
73
74namespace {
75
76// URL for account creation.
77const char kCreateAccountURL[] =
78    "https://accounts.google.com/NewAccount?service=mail";
79
80// ChromeVox tutorial URL (used in place of "getting started" url when
81// accessibility is enabled).
82const char kChromeVoxTutorialURLPattern[] =
83    "http://www.chromevox.com/tutorial/index.html?lang=%s";
84
85// Delay for transferring the auth cache to the system profile.
86const long int kAuthCacheTransferDelayMs = 2000;
87
88// Delay for restarting the ui if safe-mode login has failed.
89const long int kSafeModeRestartUiDelayMs = 30000;
90
91// Makes a call to the policy subsystem to reload the policy when we detect
92// authentication change.
93void RefreshPoliciesOnUIThread() {
94  if (g_browser_process->policy_service())
95    g_browser_process->policy_service()->RefreshPolicies(base::Closure());
96}
97
98// Copies any authentication details that were entered in the login profile in
99// the mail profile to make sure all subsystems of Chrome can access the network
100// with the provided authentication which are possibly for a proxy server.
101void TransferContextAuthenticationsOnIOThread(
102    net::URLRequestContextGetter* default_profile_context_getter,
103    net::URLRequestContextGetter* browser_process_context_getter) {
104  net::HttpAuthCache* new_cache =
105      browser_process_context_getter->GetURLRequestContext()->
106      http_transaction_factory()->GetSession()->http_auth_cache();
107  net::HttpAuthCache* old_cache =
108      default_profile_context_getter->GetURLRequestContext()->
109      http_transaction_factory()->GetSession()->http_auth_cache();
110  new_cache->UpdateAllFrom(*old_cache);
111  VLOG(1) << "Main request context populated with authentication data.";
112  // Last but not least tell the policy subsystem to refresh now as it might
113  // have been stuck until now too.
114  content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
115                                   base::Bind(&RefreshPoliciesOnUIThread));
116}
117
118}  // namespace
119
120// static
121ExistingUserController* ExistingUserController::current_controller_ = NULL;
122
123////////////////////////////////////////////////////////////////////////////////
124// ExistingUserController, public:
125
126ExistingUserController::ExistingUserController(LoginDisplayHost* host)
127    : auth_status_consumer_(NULL),
128      host_(host),
129      login_display_(host_->CreateLoginDisplay(this)),
130      num_login_attempts_(0),
131      cros_settings_(CrosSettings::Get()),
132      weak_factory_(this),
133      offline_failed_(false),
134      is_login_in_progress_(false),
135      password_changed_(false),
136      auth_mode_(LoginPerformer::AUTH_MODE_EXTENSION),
137      do_auto_enrollment_(false),
138      signin_screen_ready_(false),
139      network_state_helper_(new login::NetworkStateHelper) {
140  DCHECK(current_controller_ == NULL);
141  current_controller_ = this;
142
143  registrar_.Add(this,
144                 chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED,
145                 content::NotificationService::AllSources());
146  registrar_.Add(this,
147                 chrome::NOTIFICATION_USER_LIST_CHANGED,
148                 content::NotificationService::AllSources());
149  registrar_.Add(this,
150                 chrome::NOTIFICATION_AUTH_SUPPLIED,
151                 content::NotificationService::AllSources());
152  registrar_.Add(this,
153                 chrome::NOTIFICATION_SESSION_STARTED,
154                 content::NotificationService::AllSources());
155  show_user_names_subscription_ = cros_settings_->AddSettingsObserver(
156      kAccountsPrefShowUserNamesOnSignIn,
157      base::Bind(&ExistingUserController::DeviceSettingsChanged,
158                 base::Unretained(this)));
159  allow_new_user_subscription_ = cros_settings_->AddSettingsObserver(
160      kAccountsPrefAllowNewUser,
161      base::Bind(&ExistingUserController::DeviceSettingsChanged,
162                 base::Unretained(this)));
163  allow_guest_subscription_ = cros_settings_->AddSettingsObserver(
164      kAccountsPrefAllowGuest,
165      base::Bind(&ExistingUserController::DeviceSettingsChanged,
166                 base::Unretained(this)));
167  allow_supervised_user_subscription_ = cros_settings_->AddSettingsObserver(
168      kAccountsPrefSupervisedUsersEnabled,
169      base::Bind(&ExistingUserController::DeviceSettingsChanged,
170                 base::Unretained(this)));
171  users_subscription_ = cros_settings_->AddSettingsObserver(
172      kAccountsPrefUsers,
173      base::Bind(&ExistingUserController::DeviceSettingsChanged,
174                 base::Unretained(this)));
175  local_account_auto_login_id_subscription_ =
176      cros_settings_->AddSettingsObserver(
177          kAccountsPrefDeviceLocalAccountAutoLoginId,
178          base::Bind(&ExistingUserController::ConfigurePublicSessionAutoLogin,
179                     base::Unretained(this)));
180  local_account_auto_login_delay_subscription_ =
181      cros_settings_->AddSettingsObserver(
182          kAccountsPrefDeviceLocalAccountAutoLoginDelay,
183          base::Bind(&ExistingUserController::ConfigurePublicSessionAutoLogin,
184                     base::Unretained(this)));
185}
186
187void ExistingUserController::Init(const UserList& users) {
188  time_init_ = base::Time::Now();
189  UpdateLoginDisplay(users);
190  ConfigurePublicSessionAutoLogin();
191}
192
193void ExistingUserController::UpdateLoginDisplay(const UserList& users) {
194  bool show_users_on_signin;
195  UserList filtered_users;
196
197  cros_settings_->GetBoolean(kAccountsPrefShowUserNamesOnSignIn,
198                             &show_users_on_signin);
199  for (UserList::const_iterator it = users.begin(); it != users.end(); ++it) {
200    // TODO(xiyuan): Clean user profile whose email is not in whitelist.
201    bool meets_locally_managed_requirements =
202        (*it)->GetType() != user_manager::USER_TYPE_LOCALLY_MANAGED ||
203        UserManager::Get()->AreLocallyManagedUsersAllowed();
204    bool meets_whitelist_requirements =
205        LoginUtils::IsWhitelisted((*it)->email(), NULL) ||
206        (*it)->GetType() != user_manager::USER_TYPE_REGULAR;
207
208    // Public session accounts are always shown on login screen.
209    bool meets_show_users_requirements =
210        show_users_on_signin ||
211        (*it)->GetType() == user_manager::USER_TYPE_PUBLIC_ACCOUNT;
212    if (meets_locally_managed_requirements &&
213        meets_whitelist_requirements &&
214        meets_show_users_requirements) {
215      filtered_users.push_back(*it);
216    }
217  }
218
219  // If no user pods are visible, fallback to single new user pod which will
220  // have guest session link.
221  bool show_guest;
222  cros_settings_->GetBoolean(kAccountsPrefAllowGuest, &show_guest);
223  show_users_on_signin |= !filtered_users.empty();
224  show_guest &= !filtered_users.empty();
225  bool show_new_user = true;
226  login_display_->set_parent_window(GetNativeWindow());
227  login_display_->Init(
228      filtered_users, show_guest, show_users_on_signin, show_new_user);
229  host_->OnPreferencesChanged();
230}
231
232void ExistingUserController::DoAutoEnrollment() {
233  do_auto_enrollment_ = true;
234}
235
236void ExistingUserController::ResumeLogin() {
237  // This means the user signed-in, then auto-enrollment used his credentials
238  // to enroll and succeeded.
239  resume_login_callback_.Run();
240  resume_login_callback_.Reset();
241}
242
243////////////////////////////////////////////////////////////////////////////////
244// ExistingUserController, content::NotificationObserver implementation:
245//
246
247void ExistingUserController::Observe(
248    int type,
249    const content::NotificationSource& source,
250    const content::NotificationDetails& details) {
251  if (type == chrome::NOTIFICATION_SESSION_STARTED) {
252    // Stop listening to any notification once session has started.
253    // Sign in screen objects are marked for deletion with DeleteSoon so
254    // make sure no object would be used after session has started.
255    // http://crbug.com/125276
256    registrar_.RemoveAll();
257    return;
258  }
259  if (type == chrome::NOTIFICATION_USER_LIST_CHANGED) {
260    DeviceSettingsChanged();
261    return;
262  }
263  if (type == chrome::NOTIFICATION_AUTH_SUPPLIED) {
264    // Possibly the user has authenticated against a proxy server and we might
265    // need the credentials for enrollment and other system requests from the
266    // main |g_browser_process| request context (see bug
267    // http://crosbug.com/24861). So we transfer any credentials to the global
268    // request context here.
269    // The issue we have here is that the NOTIFICATION_AUTH_SUPPLIED is sent
270    // just after the UI is closed but before the new credentials were stored
271    // in the profile. Therefore we have to give it some time to make sure it
272    // has been updated before we copy it.
273    VLOG(1) << "Authentication was entered manually, possibly for proxyauth.";
274    scoped_refptr<net::URLRequestContextGetter> browser_process_context_getter =
275        g_browser_process->system_request_context();
276    Profile* signin_profile = ProfileHelper::GetSigninProfile();
277    scoped_refptr<net::URLRequestContextGetter> signin_profile_context_getter =
278        signin_profile->GetRequestContext();
279    DCHECK(browser_process_context_getter.get());
280    DCHECK(signin_profile_context_getter.get());
281    content::BrowserThread::PostDelayedTask(
282        content::BrowserThread::IO, FROM_HERE,
283        base::Bind(&TransferContextAuthenticationsOnIOThread,
284                   signin_profile_context_getter,
285                   browser_process_context_getter),
286        base::TimeDelta::FromMilliseconds(kAuthCacheTransferDelayMs));
287  }
288  if (type != chrome::NOTIFICATION_LOGIN_USER_IMAGE_CHANGED)
289    return;
290  login_display_->OnUserImageChanged(*content::Details<User>(details).ptr());
291}
292
293////////////////////////////////////////////////////////////////////////////////
294// ExistingUserController, private:
295
296ExistingUserController::~ExistingUserController() {
297  LoginUtils::Get()->DelegateDeleted(this);
298
299  if (current_controller_ == this) {
300    current_controller_ = NULL;
301  } else {
302    NOTREACHED() << "More than one controller are alive.";
303  }
304  DCHECK(login_display_.get());
305}
306
307////////////////////////////////////////////////////////////////////////////////
308// ExistingUserController, LoginDisplay::Delegate implementation:
309//
310
311void ExistingUserController::CancelPasswordChangedFlow() {
312  login_performer_.reset(NULL);
313  login_display_->SetUIEnabled(true);
314  StartPublicSessionAutoLoginTimer();
315}
316
317void ExistingUserController::CreateAccount() {
318  content::RecordAction(base::UserMetricsAction("Login.CreateAccount"));
319  guest_mode_url_ = google_util::AppendGoogleLocaleParam(
320      GURL(kCreateAccountURL), g_browser_process->GetApplicationLocale());
321  LoginAsGuest();
322}
323
324void ExistingUserController::CompleteLogin(const UserContext& user_context) {
325  login_display_->set_signin_completed(true);
326  if (!host_) {
327    // Complete login event was generated already from UI. Ignore notification.
328    return;
329  }
330
331  // Stop the auto-login timer when attempting login.
332  StopPublicSessionAutoLoginTimer();
333
334  // Disable UI while loading user profile.
335  login_display_->SetUIEnabled(false);
336
337  if (!time_init_.is_null()) {
338    base::TimeDelta delta = base::Time::Now() - time_init_;
339    UMA_HISTOGRAM_MEDIUM_TIMES("Login.PromptToCompleteLoginTime", delta);
340    time_init_ = base::Time();  // Reset to null.
341  }
342
343  host_->OnCompleteLogin();
344
345  // Do an ownership check now to avoid auto-enrolling if the device has
346  // already been owned.
347  DeviceSettingsService::Get()->GetOwnershipStatusAsync(
348      base::Bind(&ExistingUserController::CompleteLoginInternal,
349                 weak_factory_.GetWeakPtr(),
350                 user_context));
351}
352
353void ExistingUserController::CompleteLoginInternal(
354    const UserContext& user_context,
355    DeviceSettingsService::OwnershipStatus ownership_status) {
356  // Auto-enrollment must have made a decision by now. It's too late to enroll
357  // if the protocol isn't done at this point.
358  if (do_auto_enrollment_ &&
359      ownership_status == DeviceSettingsService::OWNERSHIP_NONE) {
360    VLOG(1) << "Forcing auto-enrollment before completing login";
361    // The only way to get out of the enrollment screen from now on is to either
362    // complete enrollment, or opt-out of it. So this controller shouldn't force
363    // enrollment again if it is reused for another sign-in.
364    do_auto_enrollment_ = false;
365    auto_enrollment_username_ = user_context.GetUserID();
366    resume_login_callback_ = base::Bind(
367        &ExistingUserController::PerformLogin,
368        weak_factory_.GetWeakPtr(),
369        user_context, LoginPerformer::AUTH_MODE_EXTENSION);
370    ShowEnrollmentScreen(true, user_context.GetUserID());
371    // Enable UI for the enrollment screen. SetUIEnabled(true) will post a
372    // request to show the sign-in screen again when invoked at the sign-in
373    // screen; invoke SetUIEnabled() after navigating to the enrollment screen.
374    login_display_->SetUIEnabled(true);
375  } else {
376    PerformLogin(user_context, LoginPerformer::AUTH_MODE_EXTENSION);
377  }
378}
379
380base::string16 ExistingUserController::GetConnectedNetworkName() {
381  return network_state_helper_->GetCurrentNetworkName();
382}
383
384bool ExistingUserController::IsSigninInProgress() const {
385  return is_login_in_progress_;
386}
387
388void ExistingUserController::Login(const UserContext& user_context,
389                                   const SigninSpecifics& specifics) {
390  if (user_context.GetUserType() == user_manager::USER_TYPE_GUEST) {
391    if (!specifics.guest_mode_url.empty()) {
392      guest_mode_url_ = GURL(specifics.guest_mode_url);
393      if (specifics.guest_mode_url_append_locale)
394        guest_mode_url_ = google_util::AppendGoogleLocaleParam(
395            guest_mode_url_, g_browser_process->GetApplicationLocale());
396    }
397    LoginAsGuest();
398    return;
399  } else if (user_context.GetUserType() ==
400             user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
401    LoginAsPublicAccount(user_context.GetUserID());
402    return;
403  } else if (user_context.GetUserType() ==
404             user_manager::USER_TYPE_RETAIL_MODE) {
405    LoginAsRetailModeUser();
406    return;
407  } else if (user_context.GetUserType() == user_manager::USER_TYPE_KIOSK_APP) {
408    LoginAsKioskApp(user_context.GetUserID(), specifics.kiosk_diagnostic_mode);
409    return;
410  }
411
412  if (!user_context.HasCredentials())
413    return;
414
415  // Stop the auto-login timer when attempting login.
416  StopPublicSessionAutoLoginTimer();
417
418  // Disable clicking on other windows.
419  login_display_->SetUIEnabled(false);
420
421  if (last_login_attempt_username_ != user_context.GetUserID()) {
422    last_login_attempt_username_ = user_context.GetUserID();
423    num_login_attempts_ = 0;
424    // Also reset state variables, which are used to determine password change.
425    offline_failed_ = false;
426    online_succeeded_for_.clear();
427  }
428  num_login_attempts_++;
429  PerformLogin(user_context, LoginPerformer::AUTH_MODE_INTERNAL);
430}
431
432void ExistingUserController::PerformLogin(
433    const UserContext& user_context,
434    LoginPerformer::AuthorizationMode auth_mode) {
435  UserManager::Get()->GetUserFlow(last_login_attempt_username_)->
436      set_host(host_);
437
438  BootTimesLoader::Get()->RecordLoginAttempted();
439
440  // Disable UI while loading user profile.
441  login_display_->SetUIEnabled(false);
442
443  // Use the same LoginPerformer for subsequent login as it has state
444  // such as Authenticator instance.
445  if (!login_performer_.get() || num_login_attempts_ <= 1) {
446    LoginPerformer::Delegate* delegate = this;
447    if (login_performer_delegate_.get())
448      delegate = login_performer_delegate_.get();
449    // Only one instance of LoginPerformer should exist at a time.
450    login_performer_.reset(NULL);
451    login_performer_.reset(new LoginPerformer(delegate));
452  }
453
454  is_login_in_progress_ = true;
455  if (gaia::ExtractDomainName(user_context.GetUserID()) ==
456      chromeos::login::kLocallyManagedUserDomain) {
457    login_performer_->LoginAsLocallyManagedUser(user_context);
458  } else {
459    login_performer_->PerformLogin(user_context, auth_mode);
460  }
461  SendAccessibilityAlert(
462      l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNING_IN));
463}
464
465void ExistingUserController::LoginAsRetailModeUser() {
466  // Stop the auto-login timer when attempting login.
467  StopPublicSessionAutoLoginTimer();
468
469  // Disable clicking on other windows.
470  login_display_->SetUIEnabled(false);
471  // TODO(rkc): Add a CHECK to make sure retail mode logins are allowed once
472  // the enterprise policy wiring is done for retail mode.
473
474  // Only one instance of LoginPerformer should exist at a time.
475  login_performer_.reset(NULL);
476  login_performer_.reset(new LoginPerformer(this));
477  is_login_in_progress_ = true;
478  login_performer_->LoginRetailMode();
479  SendAccessibilityAlert(
480      l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNIN_DEMOUSER));
481}
482
483void ExistingUserController::LoginAsGuest() {
484  if (is_login_in_progress_ || UserManager::Get()->IsUserLoggedIn())
485    return;
486
487  // Stop the auto-login timer when attempting login.
488  StopPublicSessionAutoLoginTimer();
489
490  // Disable clicking on other windows.
491  login_display_->SetUIEnabled(false);
492
493  CrosSettingsProvider::TrustedStatus status =
494      cros_settings_->PrepareTrustedValues(
495          base::Bind(&ExistingUserController::LoginAsGuest,
496                     weak_factory_.GetWeakPtr()));
497  // Must not proceed without signature verification.
498  if (status == CrosSettingsProvider::PERMANENTLY_UNTRUSTED) {
499    login_display_->ShowError(IDS_LOGIN_ERROR_OWNER_KEY_LOST, 1,
500                              HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
501    // Reenable clicking on other windows and status area.
502    login_display_->SetUIEnabled(true);
503    StartPublicSessionAutoLoginTimer();
504    display_email_.clear();
505    return;
506  } else if (status != CrosSettingsProvider::TRUSTED) {
507    // Value of AllowNewUser setting is still not verified.
508    // Another attempt will be invoked after verification completion.
509    return;
510  }
511
512  bool allow_guest;
513  cros_settings_->GetBoolean(kAccountsPrefAllowGuest, &allow_guest);
514  if (!allow_guest) {
515    // Disallowed. The UI should normally not show the guest pod but if for some
516    // reason this has been made available to the user here is the time to tell
517    // this nicely.
518    login_display_->ShowError(IDS_LOGIN_ERROR_WHITELIST, 1,
519                              HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
520    // Reenable clicking on other windows and status area.
521    login_display_->SetUIEnabled(true);
522    StartPublicSessionAutoLoginTimer();
523    display_email_.clear();
524    return;
525  }
526
527  // Only one instance of LoginPerformer should exist at a time.
528  login_performer_.reset(NULL);
529  login_performer_.reset(new LoginPerformer(this));
530  is_login_in_progress_ = true;
531  login_performer_->LoginOffTheRecord();
532  SendAccessibilityAlert(
533      l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNIN_OFFRECORD));
534}
535
536void ExistingUserController::MigrateUserData(const std::string& old_password) {
537  // LoginPerformer instance has state of the user so it should exist.
538  if (login_performer_.get())
539    login_performer_->RecoverEncryptedData(old_password);
540}
541
542void ExistingUserController::LoginAsPublicAccount(
543    const std::string& username) {
544  if (is_login_in_progress_ || UserManager::Get()->IsUserLoggedIn())
545    return;
546
547  // Stop the auto-login timer when attempting login.
548  StopPublicSessionAutoLoginTimer();
549
550  // Disable clicking on other windows.
551  login_display_->SetUIEnabled(false);
552
553  CrosSettingsProvider::TrustedStatus status =
554      cros_settings_->PrepareTrustedValues(
555          base::Bind(&ExistingUserController::LoginAsPublicAccount,
556                     weak_factory_.GetWeakPtr(),
557                     username));
558  // If device policy is permanently unavailable, logging into public accounts
559  // is not possible.
560  if (status == CrosSettingsProvider::PERMANENTLY_UNTRUSTED) {
561    login_display_->ShowError(IDS_LOGIN_ERROR_OWNER_KEY_LOST, 1,
562                              HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT);
563    // Re-enable clicking on other windows.
564    login_display_->SetUIEnabled(true);
565    return;
566  }
567
568  // If device policy is not verified yet, this function will be called again
569  // when verification finishes.
570  if (status != CrosSettingsProvider::TRUSTED)
571    return;
572
573  // If there is no public account with the given |username|, logging in is not
574  // possible.
575  const User* user = UserManager::Get()->FindUser(username);
576  if (!user || user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT) {
577    // Re-enable clicking on other windows.
578    login_display_->SetUIEnabled(true);
579    StartPublicSessionAutoLoginTimer();
580    return;
581  }
582
583  // Only one instance of LoginPerformer should exist at a time.
584  login_performer_.reset(NULL);
585  login_performer_.reset(new LoginPerformer(this));
586  is_login_in_progress_ = true;
587  login_performer_->LoginAsPublicAccount(username);
588  SendAccessibilityAlert(
589      l10n_util::GetStringUTF8(IDS_CHROMEOS_ACC_LOGIN_SIGNIN_PUBLIC_ACCOUNT));
590}
591
592void ExistingUserController::LoginAsKioskApp(const std::string& app_id,
593                                             bool diagnostic_mode) {
594  host_->StartAppLaunch(app_id, diagnostic_mode);
595}
596
597void ExistingUserController::OnSigninScreenReady() {
598  signin_screen_ready_ = true;
599  StartPublicSessionAutoLoginTimer();
600}
601
602void ExistingUserController::OnStartEnterpriseEnrollment() {
603  if (KioskAppManager::Get()->IsConsumerKioskDeviceWithAutoLaunch()) {
604    LOG(WARNING) << "Enterprise enrollment is not available after kiosk auto "
605                    "launch is set.";
606    return;
607  }
608
609  DeviceSettingsService::Get()->GetOwnershipStatusAsync(
610      base::Bind(&ExistingUserController::OnEnrollmentOwnershipCheckCompleted,
611                 weak_factory_.GetWeakPtr()));
612}
613
614void ExistingUserController::OnStartKioskEnableScreen() {
615  KioskAppManager::Get()->GetConsumerKioskAutoLaunchStatus(
616      base::Bind(
617          &ExistingUserController::OnConsumerKioskAutoLaunchCheckCompleted,
618          weak_factory_.GetWeakPtr()));
619}
620
621void ExistingUserController::OnStartKioskAutolaunchScreen() {
622  ShowKioskAutolaunchScreen();
623}
624
625void ExistingUserController::ResyncUserData() {
626  // LoginPerformer instance has state of the user so it should exist.
627  if (login_performer_.get())
628    login_performer_->ResyncEncryptedData();
629}
630
631void ExistingUserController::SetDisplayEmail(const std::string& email) {
632  display_email_ = email;
633}
634
635void ExistingUserController::ShowWrongHWIDScreen() {
636  scoped_ptr<base::DictionaryValue> params;
637  host_->StartWizard(WizardController::kWrongHWIDScreenName, params.Pass());
638}
639
640void ExistingUserController::Signout() {
641  NOTREACHED();
642}
643
644void ExistingUserController::OnConsumerKioskAutoLaunchCheckCompleted(
645    KioskAppManager::ConsumerKioskAutoLaunchStatus status) {
646  if (status == KioskAppManager::CONSUMER_KIOSK_AUTO_LAUNCH_CONFIGURABLE)
647    ShowKioskEnableScreen();
648}
649
650void ExistingUserController::OnEnrollmentOwnershipCheckCompleted(
651    DeviceSettingsService::OwnershipStatus status) {
652  if (status == DeviceSettingsService::OWNERSHIP_NONE) {
653    ShowEnrollmentScreen(false, std::string());
654  } else if (status == DeviceSettingsService::OWNERSHIP_TAKEN) {
655    // On a device that is already owned we might want to allow users to
656    // re-enroll if the policy information is invalid.
657    CrosSettingsProvider::TrustedStatus trusted_status =
658        CrosSettings::Get()->PrepareTrustedValues(
659            base::Bind(
660                &ExistingUserController::OnEnrollmentOwnershipCheckCompleted,
661                weak_factory_.GetWeakPtr(), status));
662    if (trusted_status == CrosSettingsProvider::PERMANENTLY_UNTRUSTED) {
663      ShowEnrollmentScreen(false, std::string());
664    }
665  } else {
666    // OwnershipService::GetStatusAsync is supposed to return either
667    // OWNERSHIP_NONE or OWNERSHIP_TAKEN.
668    NOTREACHED();
669  }
670}
671
672void ExistingUserController::ShowEnrollmentScreen(bool is_auto_enrollment,
673                                                  const std::string& user) {
674  scoped_ptr<base::DictionaryValue> params;
675  if (is_auto_enrollment) {
676    params.reset(new base::DictionaryValue());
677    params->SetBoolean("is_auto_enrollment", true);
678    params->SetString("user", user);
679  }
680  host_->StartWizard(WizardController::kEnrollmentScreenName,
681                     params.Pass());
682}
683
684void ExistingUserController::ShowResetScreen() {
685  scoped_ptr<base::DictionaryValue> params;
686  host_->StartWizard(WizardController::kResetScreenName, params.Pass());
687}
688
689void ExistingUserController::ShowKioskEnableScreen() {
690  scoped_ptr<base::DictionaryValue> params;
691  host_->StartWizard(WizardController::kKioskEnableScreenName, params.Pass());
692}
693
694void ExistingUserController::ShowKioskAutolaunchScreen() {
695  scoped_ptr<base::DictionaryValue> params;
696  host_->StartWizard(WizardController::kKioskAutolaunchScreenName,
697                     params.Pass());
698}
699
700void ExistingUserController::ShowTPMError() {
701  login_display_->SetUIEnabled(false);
702  login_display_->ShowErrorScreen(LoginDisplay::TPM_ERROR);
703}
704
705////////////////////////////////////////////////////////////////////////////////
706// ExistingUserController, LoginPerformer::Delegate implementation:
707//
708
709void ExistingUserController::OnAuthFailure(const AuthFailure& failure) {
710  is_login_in_progress_ = false;
711  offline_failed_ = true;
712
713  guest_mode_url_ = GURL::EmptyGURL();
714  std::string error = failure.GetErrorString();
715
716  if (UserManager::Get()->GetUserFlow(last_login_attempt_username_)->
717          HandleLoginFailure(failure)) {
718    login_display_->SetUIEnabled(true);
719    return;
720  }
721
722  if (failure.reason() == AuthFailure::OWNER_REQUIRED) {
723    ShowError(IDS_LOGIN_ERROR_OWNER_REQUIRED, error);
724    content::BrowserThread::PostDelayedTask(
725        content::BrowserThread::UI, FROM_HERE,
726        base::Bind(&SessionManagerClient::StopSession,
727                   base::Unretained(DBusThreadManager::Get()->
728                                    GetSessionManagerClient())),
729        base::TimeDelta::FromMilliseconds(kSafeModeRestartUiDelayMs));
730  } else if (failure.reason() == AuthFailure::TPM_ERROR) {
731    ShowTPMError();
732  } else if (!online_succeeded_for_.empty()) {
733    ShowGaiaPasswordChanged(online_succeeded_for_);
734  } else {
735    // Check networking after trying to login in case user is
736    // cached locally or the local admin account.
737    bool is_known_user =
738        UserManager::Get()->IsKnownUser(last_login_attempt_username_);
739    if (!network_state_helper_->IsConnected()) {
740      if (is_known_user)
741        ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error);
742      else
743        ShowError(IDS_LOGIN_ERROR_OFFLINE_FAILED_NETWORK_NOT_CONNECTED, error);
744    } else {
745      // TODO(nkostylev): Cleanup rest of ClientLogin related code.
746      if (failure.reason() == AuthFailure::NETWORK_AUTH_FAILED &&
747          failure.error().state() ==
748              GoogleServiceAuthError::HOSTED_NOT_ALLOWED) {
749        ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_HOSTED, error);
750      } else {
751        if (!is_known_user)
752          ShowError(IDS_LOGIN_ERROR_AUTHENTICATING_NEW, error);
753        else
754          ShowError(IDS_LOGIN_ERROR_AUTHENTICATING, error);
755      }
756    }
757    // Reenable clicking on other windows and status area.
758    login_display_->SetUIEnabled(true);
759    login_display_->ClearAndEnablePassword();
760    StartPublicSessionAutoLoginTimer();
761  }
762
763  // Reset user flow to default, so that special flow will not affect next
764  // attempt.
765  UserManager::Get()->ResetUserFlow(last_login_attempt_username_);
766
767  if (auth_status_consumer_)
768    auth_status_consumer_->OnAuthFailure(failure);
769
770  // Clear the recorded displayed email so it won't affect any future attempts.
771  display_email_.clear();
772}
773
774void ExistingUserController::OnAuthSuccess(const UserContext& user_context) {
775  is_login_in_progress_ = false;
776  offline_failed_ = false;
777  login_display_->set_signin_completed(true);
778
779  // Login performer will be gone so cache this value to use
780  // once profile is loaded.
781  password_changed_ = login_performer_->password_changed();
782  auth_mode_ = login_performer_->auth_mode();
783
784  UserManager::Get()->GetUserFlow(user_context.GetUserID())->
785      HandleLoginSuccess(user_context);
786
787  StopPublicSessionAutoLoginTimer();
788
789  const bool has_auth_cookies =
790      login_performer_->auth_mode() == LoginPerformer::AUTH_MODE_EXTENSION &&
791      user_context.GetAuthCode().empty();
792
793  // LoginPerformer instance will delete itself once online auth result is OK.
794  // In case of failure it'll bring up ScreenLock and ask for
795  // correct password/display error message.
796  // Even in case when following online,offline protocol and returning
797  // requests_pending = false, let LoginPerformer delete itself.
798  login_performer_->set_delegate(NULL);
799  ignore_result(login_performer_.release());
800
801  // Update user's displayed email.
802  if (!display_email_.empty()) {
803    UserManager::Get()->SaveUserDisplayEmail(user_context.GetUserID(),
804                                             display_email_);
805    display_email_.clear();
806  }
807
808  // Will call OnProfilePrepared() in the end.
809  LoginUtils::Get()->PrepareProfile(user_context,
810                                    has_auth_cookies,
811                                    false,          // Start session for user.
812                                    this);
813}
814
815void ExistingUserController::OnProfilePrepared(Profile* profile) {
816  // Reenable clicking on other windows and status area.
817  login_display_->SetUIEnabled(true);
818
819  UserManager* user_manager = UserManager::Get();
820  if (user_manager->IsCurrentUserNew() &&
821      user_manager->IsLoggedInAsLocallyManagedUser()) {
822    // Supervised users should launch into empty desktop on first run.
823    CommandLine::ForCurrentProcess()->AppendSwitch(::switches::kSilentLaunch);
824  }
825
826  if (user_manager->IsCurrentUserNew() &&
827      !user_manager->GetCurrentUserFlow()->ShouldSkipPostLoginScreens() &&
828      !WizardController::default_controller()->skip_post_login_screens()) {
829    // Don't specify start URLs if the administrator has configured the start
830    // URLs via policy.
831    if (!SessionStartupPref::TypeIsManaged(profile->GetPrefs()))
832      InitializeStartUrls();
833
834    // Mark the device as registered., i.e. the second part of OOBE as
835    // completed.
836    if (!StartupUtils::IsDeviceRegistered())
837      StartupUtils::MarkDeviceRegistered(base::Closure());
838
839    if (CommandLine::ForCurrentProcess()->HasSwitch(
840          chromeos::switches::kOobeSkipPostLogin)) {
841      LoginUtils::Get()->DoBrowserLaunch(profile, host_);
842      host_ = NULL;
843    } else {
844      ActivateWizard(WizardController::kTermsOfServiceScreenName);
845    }
846  } else {
847    LoginUtils::Get()->DoBrowserLaunch(profile, host_);
848    host_ = NULL;
849  }
850  // Inform |auth_status_consumer_| about successful login.
851  if (auth_status_consumer_)
852    auth_status_consumer_->OnAuthSuccess(UserContext());
853}
854
855void ExistingUserController::OnOffTheRecordAuthSuccess() {
856  is_login_in_progress_ = false;
857  offline_failed_ = false;
858
859  // Mark the device as registered., i.e. the second part of OOBE as completed.
860  if (!StartupUtils::IsDeviceRegistered())
861    StartupUtils::MarkDeviceRegistered(base::Closure());
862
863  LoginUtils::Get()->CompleteOffTheRecordLogin(guest_mode_url_);
864
865  if (auth_status_consumer_)
866    auth_status_consumer_->OnOffTheRecordAuthSuccess();
867}
868
869void ExistingUserController::OnPasswordChangeDetected() {
870  is_login_in_progress_ = false;
871  offline_failed_ = false;
872
873  // Must not proceed without signature verification.
874  if (CrosSettingsProvider::TRUSTED != cros_settings_->PrepareTrustedValues(
875      base::Bind(&ExistingUserController::OnPasswordChangeDetected,
876                 weak_factory_.GetWeakPtr()))) {
877    // Value of owner email is still not verified.
878    // Another attempt will be invoked after verification completion.
879    return;
880  }
881
882  if (UserManager::Get()->GetUserFlow(last_login_attempt_username_)->
883          HandlePasswordChangeDetected()) {
884    return;
885  }
886
887  // True if user has already made an attempt to enter old password and failed.
888  bool show_invalid_old_password_error =
889      login_performer_->password_changed_callback_count() > 1;
890
891  // Note: We allow owner using "full sync" mode which will recreate
892  // cryptohome and deal with owner private key being lost. This also allows
893  // us to recover from a lost owner password/homedir.
894  // TODO(gspencer): We shouldn't have to erase stateful data when
895  // doing this.  See http://crosbug.com/9115 http://crosbug.com/7792
896  login_display_->ShowPasswordChangedDialog(show_invalid_old_password_error);
897
898  if (auth_status_consumer_)
899    auth_status_consumer_->OnPasswordChangeDetected();
900
901  display_email_.clear();
902}
903
904void ExistingUserController::WhiteListCheckFailed(const std::string& email) {
905  is_login_in_progress_ = false;
906  offline_failed_ = false;
907
908  ShowError(IDS_LOGIN_ERROR_WHITELIST, email);
909
910  // Reenable clicking on other windows and status area.
911  login_display_->SetUIEnabled(true);
912  login_display_->ShowSigninUI(email);
913
914  if (auth_status_consumer_) {
915    auth_status_consumer_->OnAuthFailure(
916        AuthFailure(AuthFailure::WHITELIST_CHECK_FAILED));
917  }
918
919  display_email_.clear();
920
921  StartPublicSessionAutoLoginTimer();
922}
923
924void ExistingUserController::PolicyLoadFailed() {
925  ShowError(IDS_LOGIN_ERROR_OWNER_KEY_LOST, "");
926
927  // Reenable clicking on other windows and status area.
928  is_login_in_progress_ = false;
929  offline_failed_ = false;
930  login_display_->SetUIEnabled(true);
931
932  display_email_.clear();
933
934  // Policy load failure stops login attempts -- restart the timer.
935  StartPublicSessionAutoLoginTimer();
936}
937
938void ExistingUserController::OnOnlineChecked(const std::string& username,
939                                             bool success) {
940  if (success && last_login_attempt_username_ == username) {
941    online_succeeded_for_ = username;
942    // Wait for login attempt to end, if it hasn't yet.
943    if (offline_failed_ && !is_login_in_progress_)
944      ShowGaiaPasswordChanged(username);
945  }
946}
947
948////////////////////////////////////////////////////////////////////////////////
949// ExistingUserController, private:
950
951void ExistingUserController::DeviceSettingsChanged() {
952  if (host_ != NULL) {
953    // Signed settings or user list changed. Notify views and update them.
954    UpdateLoginDisplay(chromeos::UserManager::Get()->GetUsers());
955    ConfigurePublicSessionAutoLogin();
956    return;
957  }
958}
959
960void ExistingUserController::ActivateWizard(const std::string& screen_name) {
961  scoped_ptr<base::DictionaryValue> params;
962  host_->StartWizard(screen_name, params.Pass());
963}
964
965LoginPerformer::AuthorizationMode ExistingUserController::auth_mode() const {
966  if (login_performer_)
967    return login_performer_->auth_mode();
968
969  return auth_mode_;
970}
971
972bool ExistingUserController::password_changed() const {
973  if (login_performer_)
974    return login_performer_->password_changed();
975
976  return password_changed_;
977}
978
979void ExistingUserController::ConfigurePublicSessionAutoLogin() {
980  std::string auto_login_account_id;
981  cros_settings_->GetString(kAccountsPrefDeviceLocalAccountAutoLoginId,
982                            &auto_login_account_id);
983  const std::vector<policy::DeviceLocalAccount> device_local_accounts =
984      policy::GetDeviceLocalAccounts(cros_settings_);
985
986  public_session_auto_login_username_.clear();
987  for (std::vector<policy::DeviceLocalAccount>::const_iterator
988           it = device_local_accounts.begin();
989       it != device_local_accounts.end(); ++it) {
990    if (it->account_id == auto_login_account_id) {
991      public_session_auto_login_username_ = it->user_id;
992      break;
993    }
994  }
995
996  const User* user =
997      UserManager::Get()->FindUser(public_session_auto_login_username_);
998  if (!user || user->GetType() != user_manager::USER_TYPE_PUBLIC_ACCOUNT)
999    public_session_auto_login_username_.clear();
1000
1001  if (!cros_settings_->GetInteger(
1002          kAccountsPrefDeviceLocalAccountAutoLoginDelay,
1003          &public_session_auto_login_delay_)) {
1004    public_session_auto_login_delay_ = 0;
1005  }
1006
1007  if (!public_session_auto_login_username_.empty())
1008    StartPublicSessionAutoLoginTimer();
1009  else
1010    StopPublicSessionAutoLoginTimer();
1011}
1012
1013void ExistingUserController::ResetPublicSessionAutoLoginTimer() {
1014  // Only restart the auto-login timer if it's already running.
1015  if (auto_login_timer_ && auto_login_timer_->IsRunning()) {
1016    StopPublicSessionAutoLoginTimer();
1017    StartPublicSessionAutoLoginTimer();
1018  }
1019}
1020
1021void ExistingUserController::OnPublicSessionAutoLoginTimerFire() {
1022  CHECK(signin_screen_ready_ &&
1023        !is_login_in_progress_ &&
1024        !public_session_auto_login_username_.empty());
1025  LoginAsPublicAccount(public_session_auto_login_username_);
1026}
1027
1028void ExistingUserController::StopPublicSessionAutoLoginTimer() {
1029  if (auto_login_timer_)
1030    auto_login_timer_->Stop();
1031}
1032
1033void ExistingUserController::StartPublicSessionAutoLoginTimer() {
1034  if (!signin_screen_ready_ ||
1035      is_login_in_progress_ ||
1036      public_session_auto_login_username_.empty()) {
1037    return;
1038  }
1039
1040  // Start the auto-login timer.
1041  if (!auto_login_timer_)
1042    auto_login_timer_.reset(new base::OneShotTimer<ExistingUserController>);
1043
1044  auto_login_timer_->Start(
1045      FROM_HERE,
1046      base::TimeDelta::FromMilliseconds(
1047          public_session_auto_login_delay_),
1048      base::Bind(
1049          &ExistingUserController::OnPublicSessionAutoLoginTimerFire,
1050          weak_factory_.GetWeakPtr()));
1051}
1052
1053gfx::NativeWindow ExistingUserController::GetNativeWindow() const {
1054  return host_->GetNativeWindow();
1055}
1056
1057void ExistingUserController::InitializeStartUrls() const {
1058  std::vector<std::string> start_urls;
1059
1060  const base::ListValue *urls;
1061  UserManager* user_manager = UserManager::Get();
1062  bool can_show_getstarted_guide =
1063      user_manager->GetActiveUser()->GetType() ==
1064          user_manager::USER_TYPE_REGULAR &&
1065      !user_manager->IsCurrentUserNonCryptohomeDataEphemeral();
1066  if (user_manager->IsLoggedInAsDemoUser()) {
1067    if (CrosSettings::Get()->GetList(kStartUpUrls, &urls)) {
1068      // The retail mode user will get start URLs from a special policy if it is
1069      // set.
1070      for (base::ListValue::const_iterator it = urls->begin();
1071           it != urls->end(); ++it) {
1072        std::string url;
1073        if ((*it)->GetAsString(&url))
1074          start_urls.push_back(url);
1075      }
1076    }
1077    can_show_getstarted_guide = false;
1078  // Skip the default first-run behavior for public accounts.
1079  } else if (!user_manager->IsLoggedInAsPublicAccount()) {
1080    if (AccessibilityManager::Get()->IsSpokenFeedbackEnabled()) {
1081      const char* url = kChromeVoxTutorialURLPattern;
1082      PrefService* prefs = g_browser_process->local_state();
1083      const std::string current_locale =
1084          StringToLowerASCII(prefs->GetString(prefs::kApplicationLocale));
1085      std::string vox_url = base::StringPrintf(url, current_locale.c_str());
1086      start_urls.push_back(vox_url);
1087      can_show_getstarted_guide = false;
1088    }
1089  }
1090
1091  // Only show getting started guide for a new user.
1092  const bool should_show_getstarted_guide = user_manager->IsCurrentUserNew();
1093
1094  if (can_show_getstarted_guide && should_show_getstarted_guide) {
1095    // Don't open default Chrome window if we're going to launch the first-run
1096    // app. Because we dont' want the first-run app to be hidden in the
1097    // background.
1098    CommandLine::ForCurrentProcess()->AppendSwitch(::switches::kSilentLaunch);
1099    first_run::MaybeLaunchDialogAfterSessionStart();
1100  } else {
1101    for (size_t i = 0; i < start_urls.size(); ++i) {
1102      CommandLine::ForCurrentProcess()->AppendArg(start_urls[i]);
1103    }
1104  }
1105}
1106
1107void ExistingUserController::ShowError(int error_id,
1108                                       const std::string& details) {
1109  // TODO(dpolukhin): show detailed error info. |details| string contains
1110  // low level error info that is not localized and even is not user friendly.
1111  // For now just ignore it because error_text contains all required information
1112  // for end users, developers can see details string in Chrome logs.
1113  VLOG(1) << details;
1114  HelpAppLauncher::HelpTopic help_topic_id;
1115  bool is_offline = !network_state_helper_->IsConnected();
1116  switch (login_performer_->error().state()) {
1117    case GoogleServiceAuthError::CONNECTION_FAILED:
1118      help_topic_id = HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE;
1119      break;
1120    case GoogleServiceAuthError::ACCOUNT_DISABLED:
1121      help_topic_id = HelpAppLauncher::HELP_ACCOUNT_DISABLED;
1122      break;
1123    case GoogleServiceAuthError::HOSTED_NOT_ALLOWED:
1124      help_topic_id = HelpAppLauncher::HELP_HOSTED_ACCOUNT;
1125      break;
1126    default:
1127      help_topic_id = is_offline ?
1128          HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT_OFFLINE :
1129          HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT;
1130      break;
1131  }
1132
1133  if (error_id == IDS_LOGIN_ERROR_AUTHENTICATING) {
1134    if (num_login_attempts_ > 1) {
1135      const User* user =
1136          UserManager::Get()->FindUser(last_login_attempt_username_);
1137      if (user && (user->GetType() == user_manager::USER_TYPE_LOCALLY_MANAGED))
1138        error_id = IDS_LOGIN_ERROR_AUTHENTICATING_2ND_TIME_SUPERVISED;
1139    }
1140  }
1141
1142  login_display_->ShowError(error_id, num_login_attempts_, help_topic_id);
1143}
1144
1145void ExistingUserController::ShowGaiaPasswordChanged(
1146    const std::string& username) {
1147  // Invalidate OAuth token, since it can't be correct after password is
1148  // changed.
1149  UserManager::Get()->SaveUserOAuthStatus(
1150      username,
1151      User::OAUTH2_TOKEN_STATUS_INVALID);
1152
1153  login_display_->SetUIEnabled(true);
1154  login_display_->ShowGaiaPasswordChanged(username);
1155}
1156
1157void ExistingUserController::SendAccessibilityAlert(
1158    const std::string& alert_text) {
1159  AccessibilityAlertInfo event(ProfileHelper::GetSigninProfile(), alert_text);
1160  SendControlAccessibilityNotification(
1161      ui::AX_EVENT_VALUE_CHANGED, &event);
1162}
1163
1164}  // namespace chromeos
1165