blob: 0591c1b4d5be2691e2148e4cb00a09080b7962be [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include <signal.h>
#include <stdlib.h>
#include <sys/types.h>
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/values.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/accessibility/accessibility_manager.h"
#include "chrome/browser/chromeos/app_mode/kiosk_app_manager.h"
#include "chrome/browser/chromeos/customization_document.h"
#include "chrome/browser/chromeos/geolocation/simple_geolocation_provider.h"
#include "chrome/browser/chromeos/login/enrollment/auto_enrollment_check_screen.h"
#include "chrome/browser/chromeos/login/enrollment/enrollment_screen.h"
#include "chrome/browser/chromeos/login/existing_user_controller.h"
#include "chrome/browser/chromeos/login/helper.h"
#include "chrome/browser/chromeos/login/hwid_checker.h"
#include "chrome/browser/chromeos/login/login_utils.h"
#include "chrome/browser/chromeos/login/screens/controller_pairing_screen.h"
#include "chrome/browser/chromeos/login/screens/error_screen.h"
#include "chrome/browser/chromeos/login/screens/eula_screen.h"
#include "chrome/browser/chromeos/login/screens/hid_detection_screen.h"
#include "chrome/browser/chromeos/login/screens/host_pairing_screen.h"
#include "chrome/browser/chromeos/login/screens/kiosk_autolaunch_screen.h"
#include "chrome/browser/chromeos/login/screens/kiosk_enable_screen.h"
#include "chrome/browser/chromeos/login/screens/network_screen.h"
#include "chrome/browser/chromeos/login/screens/reset_screen.h"
#include "chrome/browser/chromeos/login/screens/terms_of_service_screen.h"
#include "chrome/browser/chromeos/login/screens/update_screen.h"
#include "chrome/browser/chromeos/login/screens/user_image_screen.h"
#include "chrome/browser/chromeos/login/screens/wrong_hwid_screen.h"
#include "chrome/browser/chromeos/login/startup_utils.h"
#include "chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/ui/oobe_display.h"
#include "chrome/browser/chromeos/net/delay_network_call.h"
#include "chrome/browser/chromeos/policy/browser_policy_connector_chromeos.h"
#include "chrome/browser/chromeos/policy/device_cloud_policy_initializer.h"
#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/timezone/timezone_provider.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/metrics/metrics_reporting_state.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/pref_names.h"
#include "chromeos/audio/cras_audio_handler.h"
#include "chromeos/chromeos_constants.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/network/network_state.h"
#include "chromeos/network/network_state_handler.h"
#include "chromeos/network/portal_detector/network_portal_detector.h"
#include "chromeos/settings/cros_settings_names.h"
#include "chromeos/settings/timezone_settings.h"
#include "components/crash/app/breakpad_linux.h"
#include "components/pairing/bluetooth_controller_pairing_controller.h"
#include "components/pairing/bluetooth_host_pairing_controller.h"
#include "components/pairing/shark_connection_listener.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_types.h"
#include "ui/base/accelerators/accelerator.h"
using content::BrowserThread;
namespace {
// If reboot didn't happen, ask user to reboot device manually.
const int kWaitForRebootTimeSec = 3;
// Interval in ms which is used for smooth screen showing.
static int kShowDelayMs = 400;
// Total timezone resolving process timeout.
const unsigned int kResolveTimeZoneTimeoutSeconds = 60;
// Stores the list of all screens that should be shown when resuming OOBE.
const char *kResumableScreens[] = {
chromeos::WizardController::kNetworkScreenName,
chromeos::WizardController::kUpdateScreenName,
chromeos::WizardController::kEulaScreenName,
chromeos::WizardController::kEnrollmentScreenName,
chromeos::WizardController::kTermsOfServiceScreenName,
chromeos::WizardController::kAutoEnrollmentCheckScreenName
};
// Checks flag for HID-detection screen show.
bool CanShowHIDDetectionScreen() {
return !CommandLine::ForCurrentProcess()->HasSwitch(
chromeos::switches::kDisableHIDDetectionOnOOBE);
}
bool IsResumableScreen(const std::string& screen) {
for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kResumableScreens); ++i) {
if (screen == kResumableScreens[i])
return true;
}
return false;
}
void RecordUMAHistogramForOOBEStepCompletionTime(std::string screen_name,
base::TimeDelta step_time) {
screen_name[0] = std::toupper(screen_name[0]);
std::string histogram_name = "OOBE.StepCompletionTime." + screen_name;
// Equivalent to using UMA_HISTOGRAM_MEDIUM_TIMES. UMA_HISTOGRAM_MEDIUM_TIMES
// can not be used here, because |histogram_name| is calculated dynamically
// and changes from call to call.
base::HistogramBase* histogram = base::Histogram::FactoryTimeGet(
histogram_name,
base::TimeDelta::FromMilliseconds(10),
base::TimeDelta::FromMinutes(3),
50,
base::HistogramBase::kUmaTargetedHistogramFlag);
histogram->AddTime(step_time);
}
bool IsRemoraRequisition() {
return g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetDeviceCloudPolicyManager()
->IsRemoraRequisition();
}
} // namespace
namespace chromeos {
const char WizardController::kNetworkScreenName[] = "network";
const char WizardController::kLoginScreenName[] = "login";
const char WizardController::kUpdateScreenName[] = "update";
const char WizardController::kUserImageScreenName[] = "image";
const char WizardController::kEulaScreenName[] = "eula";
const char WizardController::kEnrollmentScreenName[] = "enroll";
const char WizardController::kResetScreenName[] = "reset";
const char WizardController::kKioskEnableScreenName[] = "kiosk-enable";
const char WizardController::kKioskAutolaunchScreenName[] = "autolaunch";
const char WizardController::kErrorScreenName[] = "error-message";
const char WizardController::kTermsOfServiceScreenName[] = "tos";
const char WizardController::kAutoEnrollmentCheckScreenName[] =
"auto-enrollment-check";
const char WizardController::kWrongHWIDScreenName[] = "wrong-hwid";
const char WizardController::kSupervisedUserCreationScreenName[] =
"supervised-user-creation-flow";
const char WizardController::kAppLaunchSplashScreenName[] =
"app-launch-splash";
const char WizardController::kHIDDetectionScreenName[] = "hid-detection";
const char WizardController::kControllerPairingScreenName[] =
"controller-pairing";
const char WizardController::kHostPairingScreenName[] = "host-pairing";
// static
const int WizardController::kMinAudibleOutputVolumePercent = 10;
// Passing this parameter as a "first screen" initiates full OOBE flow.
const char WizardController::kOutOfBoxScreenName[] = "oobe";
// Special test value that commands not to create any window yet.
const char WizardController::kTestNoScreenName[] = "test:nowindow";
// Initialize default controller.
// static
WizardController* WizardController::default_controller_ = NULL;
// static
bool WizardController::skip_post_login_screens_ = false;
// static
bool WizardController::zero_delay_enabled_ = false;
///////////////////////////////////////////////////////////////////////////////
// WizardController, public:
PrefService* WizardController::local_state_for_testing_ = NULL;
WizardController::WizardController(chromeos::LoginDisplayHost* host,
chromeos::OobeDisplay* oobe_display)
: current_screen_(NULL),
previous_screen_(NULL),
#if defined(GOOGLE_CHROME_BUILD)
is_official_build_(true),
#else
is_official_build_(false),
#endif
is_out_of_box_(false),
host_(host),
oobe_display_(oobe_display),
usage_statistics_reporting_(true),
skip_update_enroll_after_eula_(false),
enrollment_recovery_(ShouldRecoverEnrollment()),
login_screen_started_(false),
user_image_screen_return_to_previous_hack_(false),
timezone_resolved_(false),
shark_controller_detected_(false),
weak_factory_(this) {
DCHECK(default_controller_ == NULL);
default_controller_ = this;
AccessibilityManager* accessibility_manager = AccessibilityManager::Get();
CHECK(accessibility_manager);
accessibility_subscription_ = accessibility_manager->RegisterCallback(
base::Bind(&WizardController::OnAccessibilityStatusChanged,
base::Unretained(this)));
}
WizardController::~WizardController() {
if (default_controller_ == this) {
default_controller_ = NULL;
} else {
NOTREACHED() << "More than one controller are alive.";
}
}
void WizardController::Init(
const std::string& first_screen_name,
scoped_ptr<base::DictionaryValue> screen_parameters) {
VLOG(1) << "Starting OOBE wizard with screen: " << first_screen_name;
first_screen_name_ = first_screen_name;
screen_parameters_ = screen_parameters.Pass();
bool oobe_complete = StartupUtils::IsOobeCompleted();
if (!oobe_complete || first_screen_name == kOutOfBoxScreenName)
is_out_of_box_ = true;
// This is a hacky way to check for local state corruption, because
// it depends on the fact that the local state is loaded
// synchroniously and at the first demand. IsEnterpriseManaged()
// check is required because currently powerwash is disabled for
// enterprise-entrolled devices.
//
// TODO (ygorshenin@): implement handling of the local state
// corruption in the case of asynchronious loading.
//
// TODO (ygorshenin@): remove IsEnterpriseManaged() check once
// crbug.com/241313 will be fixed.
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
if (!connector->IsEnterpriseManaged()) {
const PrefService::PrefInitializationStatus status =
GetLocalState()->GetInitializationStatus();
if (status == PrefService::INITIALIZATION_STATUS_ERROR) {
OnLocalStateInitialized(false);
return;
} else if (status == PrefService::INITIALIZATION_STATUS_WAITING) {
GetLocalState()->AddPrefInitObserver(
base::Bind(&WizardController::OnLocalStateInitialized,
weak_factory_.GetWeakPtr()));
}
}
// Use the saved screen preference from Local State.
const std::string screen_pref =
GetLocalState()->GetString(prefs::kOobeScreenPending);
if (is_out_of_box_ && !screen_pref.empty() && !IsHostPairingOobe() &&
(first_screen_name.empty() ||
first_screen_name == WizardController::kTestNoScreenName)) {
first_screen_name_ = screen_pref;
}
AdvanceToScreen(first_screen_name_);
if (!IsMachineHWIDCorrect() && !StartupUtils::IsDeviceRegistered() &&
first_screen_name_.empty())
ShowWrongHWIDScreen();
}
chromeos::ErrorScreen* WizardController::GetErrorScreen() {
return static_cast<chromeos::ErrorScreen*>(GetScreen(kErrorScreenName));
}
WizardScreen* WizardController::CreateScreen(const std::string& screen_name) {
if (screen_name == kNetworkScreenName) {
return new chromeos::NetworkScreen(this,
oobe_display_->GetNetworkScreenActor());
} else if (screen_name == kErrorScreenName) {
return new chromeos::ErrorScreen(this,
oobe_display_->GetErrorScreenActor());
} else if (screen_name == kUpdateScreenName) {
chromeos::UpdateScreen* result =
new chromeos::UpdateScreen(this, oobe_display_->GetUpdateScreenActor());
result->SetRebootCheckDelay(kWaitForRebootTimeSec);
return result;
} else if (screen_name == kUserImageScreenName) {
return new chromeos::UserImageScreen(
this, oobe_display_->GetUserImageScreenActor());
} else if (screen_name == kEulaScreenName) {
return new chromeos::EulaScreen(this, oobe_display_->GetEulaScreenActor());
} else if (screen_name == kEnrollmentScreenName) {
return new chromeos::EnrollmentScreen(
this, oobe_display_->GetEnrollmentScreenActor());
} else if (screen_name == kResetScreenName) {
return new chromeos::ResetScreen(this,
oobe_display_->GetResetScreenActor());
} else if (screen_name == kKioskEnableScreenName) {
return new chromeos::KioskEnableScreen(
this, oobe_display_->GetKioskEnableScreenActor());
} else if (screen_name == kKioskAutolaunchScreenName) {
return new chromeos::KioskAutolaunchScreen(
this, oobe_display_->GetKioskAutolaunchScreenActor());
} else if (screen_name == kTermsOfServiceScreenName) {
return new chromeos::TermsOfServiceScreen(
this, oobe_display_->GetTermsOfServiceScreenActor());
} else if (screen_name == kWrongHWIDScreenName) {
return new chromeos::WrongHWIDScreen(
this, oobe_display_->GetWrongHWIDScreenActor());
} else if (screen_name == kSupervisedUserCreationScreenName) {
return new chromeos::SupervisedUserCreationScreen(
this, oobe_display_->GetSupervisedUserCreationScreenActor());
} else if (screen_name == kHIDDetectionScreenName) {
return new chromeos::HIDDetectionScreen(
this, oobe_display_->GetHIDDetectionScreenActor());
} else if (screen_name == kAutoEnrollmentCheckScreenName) {
return new chromeos::AutoEnrollmentCheckScreen(
this, oobe_display_->GetAutoEnrollmentCheckScreenActor());
} else if (screen_name == kControllerPairingScreenName) {
if (!controller_pairing_controller_) {
controller_pairing_controller_.reset(
new pairing_chromeos::BluetoothControllerPairingController());
}
return new ControllerPairingScreen(
this, oobe_display_->GetControllerPairingScreenActor(),
controller_pairing_controller_.get());
} else if (screen_name == kHostPairingScreenName) {
if (!host_pairing_controller_) {
host_pairing_controller_.reset(
new pairing_chromeos::BluetoothHostPairingController());
host_pairing_controller_->StartPairing();
}
return new HostPairingScreen(this,
oobe_display_->GetHostPairingScreenActor(),
host_pairing_controller_.get());
}
return NULL;
}
void WizardController::ShowNetworkScreen() {
VLOG(1) << "Showing network screen.";
// Hide the status area initially; it only appears after OOBE first animates
// in. Keep it visible if the user goes back to the existing network screen.
SetStatusAreaVisible(HasScreen(kNetworkScreenName));
SetCurrentScreen(GetScreen(kNetworkScreenName));
MaybeStartListeningForSharkConnection();
}
void WizardController::ShowLoginScreen(const LoginScreenContext& context) {
if (!time_eula_accepted_.is_null()) {
base::TimeDelta delta = base::Time::Now() - time_eula_accepted_;
UMA_HISTOGRAM_MEDIUM_TIMES("OOBE.EULAToSignInTime", delta);
}
VLOG(1) << "Showing login screen.";
SetStatusAreaVisible(true);
host_->StartSignInScreen(context);
smooth_show_timer_.Stop();
oobe_display_ = NULL;
login_screen_started_ = true;
}
void WizardController::ResumeLoginScreen() {
VLOG(1) << "Resuming login screen.";
SetStatusAreaVisible(true);
host_->ResumeSignInScreen();
smooth_show_timer_.Stop();
oobe_display_ = NULL;
}
void WizardController::ShowUpdateScreen() {
VLOG(1) << "Showing update screen.";
SetStatusAreaVisible(true);
SetCurrentScreen(GetScreen(kUpdateScreenName));
}
void WizardController::ShowUserImageScreen() {
const user_manager::UserManager* user_manager =
user_manager::UserManager::Get();
// Skip user image selection for public sessions and ephemeral logins.
if (user_manager->IsLoggedInAsPublicAccount() ||
user_manager->IsCurrentUserNonCryptohomeDataEphemeral()) {
OnUserImageSkipped();
return;
}
VLOG(1) << "Showing user image screen.";
// Status area has been already shown at sign in screen so it
// doesn't make sense to hide it here and then show again at user session as
// this produces undesired UX transitions.
SetStatusAreaVisible(true);
SetCurrentScreen(GetScreen(kUserImageScreenName));
}
void WizardController::ShowEulaScreen() {
VLOG(1) << "Showing EULA screen.";
SetStatusAreaVisible(true);
SetCurrentScreen(GetScreen(kEulaScreenName));
}
void WizardController::ShowEnrollmentScreen() {
VLOG(1) << "Showing enrollment screen.";
SetStatusAreaVisible(true);
bool is_auto_enrollment = false;
std::string user;
if (screen_parameters_.get()) {
screen_parameters_->GetBoolean("is_auto_enrollment", &is_auto_enrollment);
screen_parameters_->GetString("user", &user);
}
EnrollmentScreenActor::EnrollmentMode mode =
EnrollmentScreenActor::ENROLLMENT_MODE_MANUAL;
EnrollmentScreen* screen = EnrollmentScreen::Get(this);
std::string enrollment_domain = GetForcedEnrollmentDomain();
if (is_auto_enrollment) {
mode = EnrollmentScreenActor::ENROLLMENT_MODE_AUTO;
} else if (enrollment_recovery_) {
mode = EnrollmentScreenActor::ENROLLMENT_MODE_RECOVERY;
enrollment_domain = GetEnrollmentRecoveryDomain();
} else if (ShouldAutoStartEnrollment() && !CanExitEnrollment()) {
mode = EnrollmentScreenActor::ENROLLMENT_MODE_FORCED;
}
screen->SetParameters(mode, enrollment_domain, user, auth_token_,
controller_pairing_controller_.get(),
host_pairing_controller_.get());
SetCurrentScreen(screen);
}
void WizardController::ShowResetScreen() {
VLOG(1) << "Showing reset screen.";
SetStatusAreaVisible(false);
SetCurrentScreen(GetScreen(kResetScreenName));
}
void WizardController::ShowKioskEnableScreen() {
VLOG(1) << "Showing kiosk enable screen.";
SetStatusAreaVisible(false);
SetCurrentScreen(GetScreen(kKioskEnableScreenName));
}
void WizardController::ShowKioskAutolaunchScreen() {
VLOG(1) << "Showing kiosk autolaunch screen.";
SetStatusAreaVisible(false);
SetCurrentScreen(GetScreen(kKioskAutolaunchScreenName));
}
void WizardController::ShowTermsOfServiceScreen() {
// Only show the Terms of Service when logging into a public account and Terms
// of Service have been specified through policy. In all other cases, advance
// to the user image screen immediately.
if (!user_manager::UserManager::Get()->IsLoggedInAsPublicAccount() ||
!ProfileManager::GetActiveUserProfile()->GetPrefs()->IsManagedPreference(
prefs::kTermsOfServiceURL)) {
ShowUserImageScreen();
return;
}
VLOG(1) << "Showing Terms of Service screen.";
SetStatusAreaVisible(true);
SetCurrentScreen(GetScreen(kTermsOfServiceScreenName));
}
void WizardController::ShowWrongHWIDScreen() {
VLOG(1) << "Showing wrong HWID screen.";
SetStatusAreaVisible(false);
SetCurrentScreen(GetScreen(kWrongHWIDScreenName));
}
void WizardController::ShowAutoEnrollmentCheckScreen() {
VLOG(1) << "Showing Auto-enrollment check screen.";
SetStatusAreaVisible(true);
AutoEnrollmentCheckScreen* screen = AutoEnrollmentCheckScreen::Get(this);
screen->set_auto_enrollment_controller(host_->GetAutoEnrollmentController());
SetCurrentScreen(screen);
}
void WizardController::ShowSupervisedUserCreationScreen() {
VLOG(1) << "Showing Locally managed user creation screen screen.";
SetStatusAreaVisible(true);
SetCurrentScreen(GetScreen(kSupervisedUserCreationScreenName));
}
void WizardController::ShowHIDDetectionScreen() {
VLOG(1) << "Showing HID discovery screen.";
SetStatusAreaVisible(true);
SetCurrentScreen(GetScreen(kHIDDetectionScreenName));
MaybeStartListeningForSharkConnection();
}
void WizardController::ShowControllerPairingScreen() {
VLOG(1) << "Showing controller pairing screen.";
SetStatusAreaVisible(false);
SetCurrentScreen(GetScreen(kControllerPairingScreenName));
}
void WizardController::ShowHostPairingScreen() {
VLOG(1) << "Showing host pairing screen.";
SetStatusAreaVisible(false);
SetCurrentScreen(GetScreen(kHostPairingScreenName));
}
void WizardController::SkipToLoginForTesting(
const LoginScreenContext& context) {
VLOG(1) << "SkipToLoginForTesting.";
StartupUtils::MarkEulaAccepted();
PerformPostEulaActions();
OnAutoEnrollmentCheckCompleted();
}
void WizardController::AddObserver(Observer* observer) {
observer_list_.AddObserver(observer);
}
void WizardController::RemoveObserver(Observer* observer) {
observer_list_.RemoveObserver(observer);
}
void WizardController::OnSessionStart() {
FOR_EACH_OBSERVER(Observer, observer_list_, OnSessionStart());
}
void WizardController::SkipUpdateEnrollAfterEula() {
skip_update_enroll_after_eula_ = true;
}
///////////////////////////////////////////////////////////////////////////////
// WizardController, ExitHandlers:
void WizardController::OnHIDDetectionCompleted() {
// Check for tests configuration.
if (!StartupUtils::IsOobeCompleted())
ShowNetworkScreen();
}
void WizardController::OnNetworkConnected() {
if (is_official_build_) {
if (!StartupUtils::IsEulaAccepted()) {
ShowEulaScreen();
} else {
// Possible cases:
// 1. EULA was accepted, forced shutdown/reboot during update.
// 2. EULA was accepted, planned reboot after update.
// Make sure that device is up-to-date.
InitiateOOBEUpdate();
}
} else {
InitiateOOBEUpdate();
}
}
void WizardController::OnNetworkOffline() {
// TODO(dpolukhin): if(is_out_of_box_) we cannot work offline and
// should report some error message here and stay on the same screen.
ShowLoginScreen(LoginScreenContext());
}
void WizardController::OnConnectionFailed() {
// TODO(dpolukhin): show error message after login screen is displayed.
ShowLoginScreen(LoginScreenContext());
}
void WizardController::OnUpdateCompleted() {
const bool is_shark = g_browser_process->platform_part()
->browser_policy_connector_chromeos()
->GetDeviceCloudPolicyManager()
->IsSharkRequisition();
if (is_shark) {
ShowControllerPairingScreen();
} else if (!auth_token_.empty()) {
// TODO(achuith): There is an issue with the auto enrollment check and
// remote enrollment. crbug.com/403147.
ShowEnrollmentScreen();
} else {
ShowAutoEnrollmentCheckScreen();
}
}
void WizardController::OnEulaAccepted() {
time_eula_accepted_ = base::Time::Now();
StartupUtils::MarkEulaAccepted();
InitiateMetricsReportingChange(
usage_statistics_reporting_,
base::Bind(&WizardController::InitiateMetricsReportingChangeCallback,
weak_factory_.GetWeakPtr()));
if (skip_update_enroll_after_eula_) {
PerformPostEulaActions();
ShowAutoEnrollmentCheckScreen();
} else {
InitiateOOBEUpdate();
}
}
void WizardController::InitiateMetricsReportingChangeCallback(bool enabled) {
CrosSettings::Get()->SetBoolean(kStatsReportingPref, enabled);
if (!enabled)
return;
#if defined(GOOGLE_CHROME_BUILD)
// The crash reporter initialization needs IO to complete.
base::ThreadRestrictions::ScopedAllowIO allow_io;
breakpad::InitCrashReporter(std::string());
#endif
}
void WizardController::OnUpdateErrorCheckingForUpdate() {
// TODO(nkostylev): Update should be required during OOBE.
// We do not want to block users from being able to proceed to the login
// screen if there is any error checking for an update.
// They could use "browse without sign-in" feature to set up the network to be
// able to perform the update later.
OnUpdateCompleted();
}
void WizardController::OnUpdateErrorUpdating() {
// If there was an error while getting or applying the update,
// return to network selection screen.
// TODO(nkostylev): Show message to the user explaining update error.
// TODO(nkostylev): Update should be required during OOBE.
// Temporary fix, need to migrate to new API. http://crosbug.com/4321
OnUpdateCompleted();
}
void WizardController::EnableUserImageScreenReturnToPreviousHack() {
user_image_screen_return_to_previous_hack_ = true;
}
void WizardController::OnUserImageSelected() {
if (user_image_screen_return_to_previous_hack_) {
user_image_screen_return_to_previous_hack_ = false;
DCHECK(previous_screen_);
if (previous_screen_) {
SetCurrentScreen(previous_screen_);
return;
}
}
if (!time_oobe_started_.is_null()) {
base::TimeDelta delta = base::Time::Now() - time_oobe_started_;
UMA_HISTOGRAM_CUSTOM_TIMES(
"OOBE.BootToSignInCompleted",
delta,
base::TimeDelta::FromMilliseconds(10),
base::TimeDelta::FromMinutes(30),
100);
time_oobe_started_ = base::Time();
}
// Launch browser and delete login host controller.
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&chromeos::LoginUtils::DoBrowserLaunch,
base::Unretained(chromeos::LoginUtils::Get()),
ProfileManager::GetActiveUserProfile(), host_));
host_ = NULL;
}
void WizardController::OnUserImageSkipped() {
OnUserImageSelected();
}
void WizardController::OnEnrollmentDone() {
// Mark OOBE as completed only if enterprise enrollment was part of the
// forced flow (i.e. app kiosk).
if (ShouldAutoStartEnrollment() || enrollment_recovery_)
PerformOOBECompletedActions();
// TODO(mnissler): Unify the logic for auto-login for Public Sessions and
// Kiosk Apps and make this code cover both cases: http://crbug.com/234694.
if (KioskAppManager::Get()->IsAutoLaunchEnabled())
AutoLaunchKioskApp();
else
ShowLoginScreen(LoginScreenContext());
}
void WizardController::OnResetCanceled() {
if (previous_screen_) {
SetCurrentScreen(previous_screen_);
} else {
ShowLoginScreen(LoginScreenContext());
}
}
void WizardController::OnKioskAutolaunchCanceled() {
ShowLoginScreen(LoginScreenContext());
}
void WizardController::OnKioskAutolaunchConfirmed() {
DCHECK(KioskAppManager::Get()->IsAutoLaunchEnabled());
AutoLaunchKioskApp();
}
void WizardController::OnKioskEnableCompleted() {
ShowLoginScreen(LoginScreenContext());
}
void WizardController::OnWrongHWIDWarningSkipped() {
if (previous_screen_)
SetCurrentScreen(previous_screen_);
else
ShowLoginScreen(LoginScreenContext());
}
void WizardController::OnAutoEnrollmentDone() {
VLOG(1) << "Automagic enrollment done, resuming previous signin";
ResumeLoginScreen();
}
void WizardController::OnAutoEnrollmentCheckCompleted() {
if (ShouldAutoStartEnrollment() || enrollment_recovery_) {
ShowEnrollmentScreen();
} else {
PerformOOBECompletedActions();
ShowLoginScreen(LoginScreenContext());
}
}
void WizardController::OnTermsOfServiceDeclined() {
// If the user declines the Terms of Service, end the session and return to
// the login screen.
DBusThreadManager::Get()->GetSessionManagerClient()->StopSession();
}
void WizardController::OnTermsOfServiceAccepted() {
// If the user accepts the Terms of Service, advance to the user image screen.
ShowUserImageScreen();
}
void WizardController::OnControllerPairingFinished() {
ShowAutoEnrollmentCheckScreen();
}
void WizardController::OnHostPairingFinished() {
InitiateOOBEUpdate();
}
void WizardController::InitiateOOBEUpdate() {
VLOG(1) << "InitiateOOBEUpdate";
PerformPostEulaActions();
SetCurrentScreenSmooth(GetScreen(kUpdateScreenName), true);
UpdateScreen::Get(this)->StartNetworkCheck();
}
void WizardController::StartTimezoneResolve() {
geolocation_provider_.reset(new SimpleGeolocationProvider(
g_browser_process->system_request_context(),
SimpleGeolocationProvider::DefaultGeolocationProviderURL()));
geolocation_provider_->RequestGeolocation(
base::TimeDelta::FromSeconds(kResolveTimeZoneTimeoutSeconds),
base::Bind(&WizardController::OnLocationResolved,
weak_factory_.GetWeakPtr()));
}
void WizardController::PerformPostEulaActions() {
DelayNetworkCall(
base::Bind(&WizardController::StartTimezoneResolve,
weak_factory_.GetWeakPtr()),
base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS));
DelayNetworkCall(
ServicesCustomizationDocument::GetInstance()
->EnsureCustomizationAppliedClosure(),
base::TimeDelta::FromMilliseconds(kDefaultNetworkRetryDelayMS));
// Now that EULA has been accepted (for official builds), enable portal check.
// ChromiumOS builds would go though this code path too.
NetworkHandler::Get()->network_state_handler()->SetCheckPortalList(
NetworkStateHandler::kDefaultCheckPortalList);
host_->GetAutoEnrollmentController()->Start();
host_->PrewarmAuthentication();
NetworkPortalDetector::Get()->Enable(true);
}
void WizardController::PerformOOBECompletedActions() {
UMA_HISTOGRAM_COUNTS_100(
"HIDDetection.TimesDialogShownPerOOBECompleted",
GetLocalState()->GetInteger(prefs::kTimesHIDDialogShown));
GetLocalState()->ClearPref(prefs::kTimesHIDDialogShown);
StartupUtils::MarkOobeCompleted();
if (enrollment_recovery_)
chrome::AttemptRestart();
}
void WizardController::SetCurrentScreen(WizardScreen* new_current) {
SetCurrentScreenSmooth(new_current, false);
}
void WizardController::ShowCurrentScreen() {
// ShowCurrentScreen may get called by smooth_show_timer_ even after
// flow has been switched to sign in screen (ExistingUserController).
if (!oobe_display_)
return;
// First remember how far have we reached so that we can resume if needed.
if (is_out_of_box_ && IsResumableScreen(current_screen_->GetName()))
StartupUtils::SaveOobePendingScreen(current_screen_->GetName());
smooth_show_timer_.Stop();
FOR_EACH_OBSERVER(Observer, observer_list_, OnScreenChanged(current_screen_));
current_screen_->Show();
}
void WizardController::SetCurrentScreenSmooth(WizardScreen* new_current,
bool use_smoothing) {
if (current_screen_ == new_current ||
new_current == NULL ||
oobe_display_ == NULL) {
return;
}
smooth_show_timer_.Stop();
if (current_screen_)
current_screen_->Hide();
std::string screen_id = new_current->GetName();
if (IsOOBEStepToTrack(screen_id))
screen_show_times_[screen_id] = base::Time::Now();
previous_screen_ = current_screen_;
current_screen_ = new_current;
if (use_smoothing) {
smooth_show_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kShowDelayMs),
this,
&WizardController::ShowCurrentScreen);
} else {
ShowCurrentScreen();
}
}
void WizardController::SetStatusAreaVisible(bool visible) {
host_->SetStatusAreaVisible(visible);
}
void WizardController::OnHIDScreenNecessityCheck(bool screen_needed) {
if (!oobe_display_)
return;
if (screen_needed)
ShowHIDDetectionScreen();
else
ShowNetworkScreen();
}
void WizardController::AdvanceToScreen(const std::string& screen_name) {
if (screen_name == kNetworkScreenName) {
ShowNetworkScreen();
} else if (screen_name == kLoginScreenName) {
ShowLoginScreen(LoginScreenContext());
} else if (screen_name == kUpdateScreenName) {
InitiateOOBEUpdate();
} else if (screen_name == kUserImageScreenName) {
ShowUserImageScreen();
} else if (screen_name == kEulaScreenName) {
ShowEulaScreen();
} else if (screen_name == kResetScreenName) {
ShowResetScreen();
} else if (screen_name == kKioskEnableScreenName) {
ShowKioskEnableScreen();
} else if (screen_name == kKioskAutolaunchScreenName) {
ShowKioskAutolaunchScreen();
} else if (screen_name == kEnrollmentScreenName) {
ShowEnrollmentScreen();
} else if (screen_name == kTermsOfServiceScreenName) {
ShowTermsOfServiceScreen();
} else if (screen_name == kWrongHWIDScreenName) {
ShowWrongHWIDScreen();
} else if (screen_name == kAutoEnrollmentCheckScreenName) {
ShowAutoEnrollmentCheckScreen();
} else if (screen_name == kSupervisedUserCreationScreenName) {
ShowSupervisedUserCreationScreen();
} else if (screen_name == kAppLaunchSplashScreenName) {
AutoLaunchKioskApp();
} else if (screen_name == kHIDDetectionScreenName) {
ShowHIDDetectionScreen();
} else if (screen_name == kControllerPairingScreenName) {
ShowControllerPairingScreen();
} else if (screen_name == kHostPairingScreenName) {
ShowHostPairingScreen();
} else if (screen_name != kTestNoScreenName) {
if (is_out_of_box_) {
time_oobe_started_ = base::Time::Now();
if (IsHostPairingOobe()) {
ShowHostPairingScreen();
} else if (CanShowHIDDetectionScreen()) {
base::Callback<void(bool)> on_check = base::Bind(
&WizardController::OnHIDScreenNecessityCheck,
weak_factory_.GetWeakPtr());
oobe_display_->GetHIDDetectionScreenActor()->CheckIsScreenRequired(
on_check);
} else {
ShowNetworkScreen();
}
} else {
ShowLoginScreen(LoginScreenContext());
}
}
}
///////////////////////////////////////////////////////////////////////////////
// WizardController, chromeos::ScreenObserver overrides:
void WizardController::OnExit(ExitCodes exit_code) {
VLOG(1) << "Wizard screen exit code: " << exit_code;
std::string previous_screen_id = current_screen_->GetName();
if (IsOOBEStepToTrack(previous_screen_id)) {
RecordUMAHistogramForOOBEStepCompletionTime(
previous_screen_id,
base::Time::Now() - screen_show_times_[previous_screen_id]);
}
switch (exit_code) {
case HID_DETECTION_COMPLETED:
OnHIDDetectionCompleted();
break;
case NETWORK_CONNECTED:
OnNetworkConnected();
break;
case CONNECTION_FAILED:
OnConnectionFailed();
break;
case UPDATE_INSTALLED:
case UPDATE_NOUPDATE:
OnUpdateCompleted();
break;
case UPDATE_ERROR_CHECKING_FOR_UPDATE:
OnUpdateErrorCheckingForUpdate();
break;
case UPDATE_ERROR_UPDATING:
OnUpdateErrorUpdating();
break;
case USER_IMAGE_SELECTED:
OnUserImageSelected();
break;
case EULA_ACCEPTED:
OnEulaAccepted();
break;
case EULA_BACK:
ShowNetworkScreen();
break;
case ENTERPRISE_AUTO_ENROLLMENT_CHECK_COMPLETED:
if (skip_update_enroll_after_eula_)
ShowEnrollmentScreen();
else
OnAutoEnrollmentCheckCompleted();
break;
case ENTERPRISE_ENROLLMENT_COMPLETED:
OnEnrollmentDone();
break;
case ENTERPRISE_ENROLLMENT_BACK:
ShowNetworkScreen();
break;
case RESET_CANCELED:
OnResetCanceled();
break;
case KIOSK_AUTOLAUNCH_CANCELED:
OnKioskAutolaunchCanceled();
break;
case KIOSK_AUTOLAUNCH_CONFIRMED:
OnKioskAutolaunchConfirmed();
break;
case KIOSK_ENABLE_COMPLETED:
OnKioskEnableCompleted();
break;
case ENTERPRISE_AUTO_MAGIC_ENROLLMENT_COMPLETED:
OnAutoEnrollmentDone();
break;
case TERMS_OF_SERVICE_DECLINED:
OnTermsOfServiceDeclined();
break;
case TERMS_OF_SERVICE_ACCEPTED:
OnTermsOfServiceAccepted();
break;
case WRONG_HWID_WARNING_SKIPPED:
OnWrongHWIDWarningSkipped();
break;
case CONTROLLER_PAIRING_FINISHED:
OnControllerPairingFinished();
break;
case HOST_PAIRING_FINISHED:
OnHostPairingFinished();
break;
default:
NOTREACHED();
}
}
void WizardController::OnSetUserNamePassword(const std::string& username,
const std::string& password) {
username_ = username;
password_ = password;
}
void WizardController::SetUsageStatisticsReporting(bool val) {
usage_statistics_reporting_ = val;
}
bool WizardController::GetUsageStatisticsReporting() const {
return usage_statistics_reporting_;
}
void WizardController::ShowErrorScreen() {
VLOG(1) << "Showing error screen.";
SetCurrentScreen(GetScreen(kErrorScreenName));
}
void WizardController::HideErrorScreen(WizardScreen* parent_screen) {
DCHECK(parent_screen);
VLOG(1) << "Hiding error screen.";
SetCurrentScreen(parent_screen);
}
void WizardController::OnAccessibilityStatusChanged(
const AccessibilityStatusEventDetails& details) {
enum AccessibilityNotificationType type = details.notification_type;
if (type == ACCESSIBILITY_MANAGER_SHUTDOWN) {
accessibility_subscription_.reset();
return;
} else if (type != ACCESSIBILITY_TOGGLE_SPOKEN_FEEDBACK || !details.enabled) {
return;
}
CrasAudioHandler* cras = CrasAudioHandler::Get();
if (cras->IsOutputMuted()) {
cras->SetOutputMute(false);
cras->SetOutputVolumePercent(kMinAudibleOutputVolumePercent);
} else if (cras->GetOutputVolumePercent() < kMinAudibleOutputVolumePercent) {
cras->SetOutputVolumePercent(kMinAudibleOutputVolumePercent);
}
}
void WizardController::AutoLaunchKioskApp() {
KioskAppManager::App app_data;
std::string app_id = KioskAppManager::Get()->GetAutoLaunchApp();
CHECK(KioskAppManager::Get()->GetApp(app_id, &app_data));
host_->StartAppLaunch(app_id, false /* diagnostic_mode */);
}
// static
void WizardController::SetZeroDelays() {
kShowDelayMs = 0;
zero_delay_enabled_ = true;
}
// static
bool WizardController::IsZeroDelayEnabled() {
return zero_delay_enabled_;
}
// static
bool WizardController::IsOOBEStepToTrack(const std::string& screen_id) {
return (screen_id == kHIDDetectionScreenName ||
screen_id == kNetworkScreenName ||
screen_id == kUpdateScreenName ||
screen_id == kUserImageScreenName ||
screen_id == kEulaScreenName ||
screen_id == kLoginScreenName ||
screen_id == kWrongHWIDScreenName);
}
// static
void WizardController::SkipPostLoginScreensForTesting() {
skip_post_login_screens_ = true;
}
// static
bool WizardController::ShouldAutoStartEnrollment() {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
policy::DeviceCloudPolicyInitializer* dcp_initializer =
connector->GetDeviceCloudPolicyInitializer();
return dcp_initializer && dcp_initializer->ShouldAutoStartEnrollment();
}
// static
bool WizardController::ShouldRecoverEnrollment() {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
policy::DeviceCloudPolicyInitializer* dcp_initializer =
connector->GetDeviceCloudPolicyInitializer();
return dcp_initializer && dcp_initializer->ShouldRecoverEnrollment();
}
// static
std::string WizardController::GetEnrollmentRecoveryDomain() {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
policy::DeviceCloudPolicyInitializer* dcp_initializer =
connector->GetDeviceCloudPolicyInitializer();
if (!dcp_initializer)
return std::string();
return dcp_initializer->GetEnrollmentRecoveryDomain();
}
// static
bool WizardController::CanExitEnrollment() {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
CHECK(connector);
return connector->GetDeviceCloudPolicyInitializer()->CanExitEnrollment();
}
// static
std::string WizardController::GetForcedEnrollmentDomain() {
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
CHECK(connector);
return connector->GetDeviceCloudPolicyInitializer()
->GetForcedEnrollmentDomain();
}
void WizardController::OnLocalStateInitialized(bool /* succeeded */) {
if (GetLocalState()->GetInitializationStatus() !=
PrefService::INITIALIZATION_STATUS_ERROR) {
return;
}
GetErrorScreen()->SetUIState(ErrorScreen::UI_STATE_LOCAL_STATE_ERROR);
SetStatusAreaVisible(false);
ShowErrorScreen();
}
PrefService* WizardController::GetLocalState() {
if (local_state_for_testing_)
return local_state_for_testing_;
return g_browser_process->local_state();
}
void WizardController::OnTimezoneResolved(
scoped_ptr<TimeZoneResponseData> timezone,
bool server_error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(timezone.get());
// To check that "this" is not destroyed try to access some member
// (timezone_provider_) in this case. Expect crash here.
DCHECK(timezone_provider_.get());
timezone_resolved_ = true;
base::ScopedClosureRunner inform_test(on_timezone_resolved_for_testing_);
on_timezone_resolved_for_testing_.Reset();
VLOG(1) << "Resolved local timezone={" << timezone->ToStringForDebug()
<< "}.";
if (timezone->status != TimeZoneResponseData::OK) {
LOG(WARNING) << "Resolve TimeZone: failed to resolve timezone.";
return;
}
policy::BrowserPolicyConnectorChromeOS* connector =
g_browser_process->platform_part()->browser_policy_connector_chromeos();
if (connector->IsEnterpriseManaged()) {
std::string policy_timezone;
if (chromeos::CrosSettings::Get()->GetString(
chromeos::kSystemTimezonePolicy, &policy_timezone) &&
!policy_timezone.empty()) {
VLOG(1) << "Resolve TimeZone: TimeZone settings are overridden"
<< " by DevicePolicy.";
return;
}
}
if (!timezone->timeZoneId.empty()) {
VLOG(1) << "Resolve TimeZone: setting timezone to '" << timezone->timeZoneId
<< "'";
chromeos::system::TimezoneSettings::GetInstance()->SetTimezoneFromID(
base::UTF8ToUTF16(timezone->timeZoneId));
}
}
TimeZoneProvider* WizardController::GetTimezoneProvider() {
if (!timezone_provider_) {
timezone_provider_.reset(
new TimeZoneProvider(g_browser_process->system_request_context(),
DefaultTimezoneProviderURL()));
}
return timezone_provider_.get();
}
void WizardController::OnLocationResolved(const Geoposition& position,
bool server_error,
const base::TimeDelta elapsed) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
const base::TimeDelta timeout =
base::TimeDelta::FromSeconds(kResolveTimeZoneTimeoutSeconds);
// Ignore invalid position.
if (!position.Valid())
return;
if (elapsed >= timeout) {
LOG(WARNING) << "Resolve TimeZone: got location after timeout ("
<< elapsed.InSecondsF() << " seconds elapsed). Ignored.";
return;
}
// WizardController owns TimezoneProvider, so timezone request is silently
// cancelled on destruction.
GetTimezoneProvider()->RequestTimezone(
position,
false, // sensor
timeout - elapsed,
base::Bind(&WizardController::OnTimezoneResolved,
base::Unretained(this)));
}
bool WizardController::SetOnTimeZoneResolvedForTesting(
const base::Closure& callback) {
if (timezone_resolved_)
return false;
on_timezone_resolved_for_testing_ = callback;
return true;
}
bool WizardController::IsHostPairingOobe() const {
return IsRemoraRequisition() &&
(CommandLine::ForCurrentProcess()->HasSwitch(switches::kHostPairingOobe) ||
shark_controller_detected_);
}
void WizardController::MaybeStartListeningForSharkConnection() {
if (!IsRemoraRequisition())
return;
// We shouldn't be here if we are running pairing OOBE already.
DCHECK(!IsHostPairingOobe());
if (!shark_connection_listener_) {
shark_connection_listener_.reset(
new pairing_chromeos::SharkConnectionListener(
base::Bind(&WizardController::OnSharkConnected,
weak_factory_.GetWeakPtr())));
}
}
void WizardController::OnSharkConnected(
scoped_ptr<pairing_chromeos::HostPairingController> pairing_controller) {
VLOG(1) << "OnSharkConnected";
host_pairing_controller_ = pairing_controller.Pass();
base::MessageLoop::current()->DeleteSoon(
FROM_HERE, shark_connection_listener_.release());
shark_controller_detected_ = true;
ShowHostPairingScreen();
}
} // namespace chromeos