blob: a06a163b843d64ededf7813354a96dd34f13ef08 [file] [log] [blame]
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ash/login/demo_mode/demo_setup_controller.h"
#include <utility>
#include "ash/components/arc/arc_util.h"
#include "ash/constants/ash_features.h"
#include "ash/constants/ash_pref_names.h"
#include "ash/constants/ash_switches.h"
#include "base/barrier_closure.h"
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/metrics/histogram_macros.h"
#include "base/strings/stringprintf.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/time/time.h"
#include "chrome/browser/ash/login/demo_mode/demo_components.h"
#include "chrome/browser/ash/login/demo_mode/demo_session.h"
#include "chrome/browser/ash/login/startup_utils.h"
#include "chrome/browser/ash/login/wizard_controller.h"
#include "chrome/browser/ash/policy/enrollment/enrollment_config.h"
#include "chrome/browser/ash/policy/enrollment/enrollment_requisition_manager.h"
#include "chrome/browser/ash/policy/enrollment/enrollment_status.h"
#include "chrome/browser/browser_process.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/ash/components/dbus/dbus_thread_manager.h"
#include "chromeos/ash/components/demo_mode/utils/demo_session_utils.h"
#include "chromeos/ash/components/demo_mode/utils/dimensions_utils.h"
#include "chromeos/ash/components/growth/campaigns_manager.h"
#include "chromeos/ash/components/install_attributes/install_attributes.h"
#include "chromeos/constants/chromeos_features.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "ui/base/l10n/l10n_util.h"
namespace ash {
namespace {
using ErrorCode = DemoSetupController::DemoSetupError::ErrorCode;
using RecoveryMethod = DemoSetupController::DemoSetupError::RecoveryMethod;
constexpr char kDemoSetupDownloadDurationHistogram[] =
"DemoMode.Setup.DownloadDuration";
constexpr char kDemoSetupEnrollDurationHistogram[] =
"DemoMode.Setup.EnrollDuration";
constexpr char kDemoSetupLoadingDurationHistogram[] =
"DemoMode.Setup.LoadingDuration";
constexpr char kDemoSetupNumRetriesHistogram[] = "DemoMode.Setup.NumRetries";
constexpr char kDemoSetupComponentInitialLoadingResultHistogram[] =
"DemoMode.Setup.ComponentInitialLoadingResult";
constexpr char kDemoSetupComponentLoadingRetryResultHistogram[] =
"DemoMode.Setup.ComponentLoadingRetryResult";
constexpr char kDemoSetupErrorHistogram[] = "DemoMode.Setup.Error";
struct DemoSetupStepInfo {
DemoSetupController::DemoSetupStep step;
const int step_index;
};
base::span<const DemoSetupStepInfo> GetDemoSetupStepsInfo() {
static const DemoSetupStepInfo kDemoModeSetupStepsInfo[] = {
{DemoSetupController::DemoSetupStep::kDownloadResources, 0},
{DemoSetupController::DemoSetupStep::kEnrollment, 1},
{DemoSetupController::DemoSetupStep::kComplete, 2}};
return kDemoModeSetupStepsInfo;
}
DemoSetupController::DemoSetupError CreateFromClientStatus(
policy::DeviceManagementStatus status,
const std::string& debug_message) {
switch (status) {
case policy::DM_STATUS_SUCCESS:
return DemoSetupController::DemoSetupError(
ErrorCode::kUnexpectedError, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_REQUEST_INVALID:
return DemoSetupController::DemoSetupError(
ErrorCode::kInvalidRequest, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_REQUEST_FAILED:
return DemoSetupController::DemoSetupError(
ErrorCode::kRequestNetworkError, RecoveryMethod::kCheckNetwork,
debug_message);
case policy::DM_STATUS_TEMPORARY_UNAVAILABLE:
return DemoSetupController::DemoSetupError(
ErrorCode::kTemporaryUnavailable, RecoveryMethod::kRetry,
debug_message);
case policy::DM_STATUS_HTTP_STATUS_ERROR:
case policy::DM_STATUS_REQUEST_TOO_LARGE:
return DemoSetupController::DemoSetupError(
ErrorCode::kResponseError, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_RESPONSE_DECODING_ERROR:
return DemoSetupController::DemoSetupError(
ErrorCode::kResponseDecodingError, RecoveryMethod::kUnknown,
debug_message);
case policy::DM_STATUS_SERVICE_MANAGEMENT_NOT_SUPPORTED:
case policy::DM_STATUS_SERVICE_CONSUMER_ACCOUNT_WITH_PACKAGED_LICENSE:
case policy::DM_STATUS_SERVICE_ACTIVATION_PENDING:
case policy::DM_STATUS_SERVICE_ENTERPRISE_ACCOUNT_IS_NOT_ELIGIBLE_TO_ENROLL:
case policy::DM_STATUS_SERVICE_ENTERPRISE_TOS_HAS_NOT_BEEN_ACCEPTED:
case policy::DM_STATUS_SERVICE_ILLEGAL_ACCOUNT_FOR_PACKAGED_EDU_LICENSE:
case policy::DM_STATUS_SERVICE_INVALID_PACKAGED_DEVICE_FOR_KIOSK:
return DemoSetupController::DemoSetupError(ErrorCode::kDemoAccountError,
RecoveryMethod::kUnknown,
debug_message);
case policy::DM_STATUS_SERVICE_DEVICE_NOT_FOUND:
case policy::DM_STATUS_SERVICE_DEVICE_NEEDS_RESET:
return DemoSetupController::DemoSetupError(
ErrorCode::kDeviceNotFound, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_MANAGEMENT_TOKEN_INVALID:
return DemoSetupController::DemoSetupError(
ErrorCode::kInvalidDMToken, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_INVALID_SERIAL_NUMBER:
return DemoSetupController::DemoSetupError(
ErrorCode::kInvalidSerialNumber, RecoveryMethod::kUnknown,
debug_message);
case policy::DM_STATUS_SERVICE_DEVICE_ID_CONFLICT:
return DemoSetupController::DemoSetupError(
ErrorCode::kDeviceIdError, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_TOO_MANY_REQUESTS:
return DemoSetupController::DemoSetupError(
ErrorCode::kTooManyRequestsError, RecoveryMethod::kRetry,
debug_message);
case policy::DM_STATUS_SERVICE_MISSING_LICENSES:
return DemoSetupController::DemoSetupError(
ErrorCode::kLicenseError, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_DEPROVISIONED:
return DemoSetupController::DemoSetupError(
ErrorCode::kDeviceDeprovisioned, RecoveryMethod::kUnknown,
debug_message);
case policy::DM_STATUS_SERVICE_DOMAIN_MISMATCH:
return DemoSetupController::DemoSetupError(
ErrorCode::kDomainMismatch, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_CANNOT_SIGN_REQUEST:
return DemoSetupController::DemoSetupError(
ErrorCode::kSigningError, RecoveryMethod::kPowerwash, debug_message);
case policy::DM_STATUS_SERVICE_POLICY_NOT_FOUND:
return DemoSetupController::DemoSetupError(
ErrorCode::kPolicyNotFound, RecoveryMethod::kUnknown, debug_message);
case policy::DM_STATUS_SERVICE_ARC_DISABLED:
return DemoSetupController::DemoSetupError(
ErrorCode::kArcError, RecoveryMethod::kUnknown, debug_message);
}
NOTREACHED() << "Demo mode setup received unsupported client status";
}
DemoSetupController::DemoSetupError CreateFromLockStatus(
InstallAttributes::LockResult status,
const std::string& debug_message) {
switch (status) {
case InstallAttributes::LOCK_SUCCESS:
case InstallAttributes::LOCK_NOT_READY:
return DemoSetupController::DemoSetupError(
ErrorCode::kUnexpectedError, RecoveryMethod::kUnknown, debug_message);
case InstallAttributes::LOCK_TIMEOUT:
return DemoSetupController::DemoSetupError(
ErrorCode::kLockTimeout, RecoveryMethod::kReboot, debug_message);
case InstallAttributes::LOCK_BACKEND_INVALID:
case InstallAttributes::LOCK_SET_ERROR:
case InstallAttributes::LOCK_FINALIZE_ERROR:
case InstallAttributes::LOCK_READBACK_ERROR:
return DemoSetupController::DemoSetupError(
ErrorCode::kLockError, RecoveryMethod::kPowerwash, debug_message);
case InstallAttributes::LOCK_ALREADY_LOCKED:
case InstallAttributes::LOCK_WRONG_DOMAIN:
case InstallAttributes::LOCK_WRONG_MODE:
return DemoSetupController::DemoSetupError(
ErrorCode::kAlreadyLocked, RecoveryMethod::kPowerwash, debug_message);
}
NOTREACHED() << "Demo mode setup received unsupported lock status";
}
} // namespace
// static
DemoSetupController::DemoSetupError
DemoSetupController::DemoSetupError::CreateFromEnrollmentStatus(
const policy::EnrollmentStatus& status) {
const std::string debug_message = base::StringPrintf(
"EnrollmentError: (code: %d, client_status: %d, store_status: %d, "
"validation_status: %d, lock_status: %d)",
static_cast<int>(status.enrollment_code()), status.client_status(),
status.store_status(), status.validation_status(), status.lock_status());
switch (status.enrollment_code()) {
case policy::EnrollmentStatus::Code::kSuccess:
return DemoSetupError(ErrorCode::kUnexpectedError,
RecoveryMethod::kUnknown, debug_message);
case policy::EnrollmentStatus::Code::kNoStateKeys:
return DemoSetupError(ErrorCode::kNoStateKeys, RecoveryMethod::kReboot,
debug_message);
case policy::EnrollmentStatus::Code::kRegistrationFailed:
return CreateFromClientStatus(status.client_status(), debug_message);
case policy::EnrollmentStatus::Code::kRobotAuthFetchFailed:
case policy::EnrollmentStatus::Code::kRobotRefreshFetchFailed:
return DemoSetupError(ErrorCode::kRobotFetchError,
RecoveryMethod::kCheckNetwork, debug_message);
case policy::EnrollmentStatus::Code::kRobotRefreshStoreFailed:
return DemoSetupError(ErrorCode::kRobotStoreError,
RecoveryMethod::kReboot, debug_message);
case policy::EnrollmentStatus::Code::kRegistrationBadMode:
return DemoSetupError(ErrorCode::kBadMode, RecoveryMethod::kRetry,
debug_message);
case policy::EnrollmentStatus::Code::kRegistrationCertFetchFailed:
return DemoSetupError(ErrorCode::kCertFetchError, RecoveryMethod::kRetry,
debug_message);
case policy::EnrollmentStatus::Code::kPolicyFetchFailed:
return DemoSetupError(ErrorCode::kPolicyFetchError,
RecoveryMethod::kRetry, debug_message);
case policy::EnrollmentStatus::Code::kValidationFailed:
return DemoSetupError(ErrorCode::kPolicyValidationError,
RecoveryMethod::kRetry, debug_message);
case policy::EnrollmentStatus::Code::kLockError:
return CreateFromLockStatus(status.lock_status(), debug_message);
case policy::EnrollmentStatus::Code::kStoreError:
return DemoSetupError(ErrorCode::kOnlineStoreError,
RecoveryMethod::kRetry, debug_message);
case policy::EnrollmentStatus::Code::kAttributeUpdateFailed:
return DemoSetupError(ErrorCode::kUnexpectedError,
RecoveryMethod::kUnknown, debug_message);
case policy::EnrollmentStatus::Code::kNoMachineIdentification:
return DemoSetupError(ErrorCode::kMachineIdentificationError,
RecoveryMethod::kUnknown, debug_message);
case policy::EnrollmentStatus::Code::kDmTokenStoreFailed:
return DemoSetupError(ErrorCode::kDMTokenStoreError,
RecoveryMethod::kUnknown, debug_message);
case policy::EnrollmentStatus::Code::kMayNotBlockDevMode:
return DemoSetupError(ErrorCode::kUnexpectedError,
RecoveryMethod::kUnknown, debug_message);
}
NOTREACHED() << "Demo mode setup received unsupported enrollment status";
}
// static
DemoSetupController::DemoSetupError
DemoSetupController::DemoSetupError::CreateFromOtherEnrollmentError(
EnrollmentLauncher::OtherError error) {
const std::string debug_message =
base::StringPrintf("Other error: %d", error);
switch (error) {
case EnrollmentLauncher::OTHER_ERROR_DOMAIN_MISMATCH:
return DemoSetupError(ErrorCode::kAlreadyLocked,
RecoveryMethod::kPowerwash, debug_message);
case EnrollmentLauncher::OTHER_ERROR_FATAL:
return DemoSetupError(ErrorCode::kUnexpectedError,
RecoveryMethod::kUnknown, debug_message);
}
NOTREACHED() << "Demo mode setup received unsupported enrollment error";
}
// static
DemoSetupController::DemoSetupError
DemoSetupController::DemoSetupError::CreateFromComponentError(
component_updater::ComponentManagerAsh::Error error,
std::string component_name) {
const std::string debug_message =
base::StringPrintf("Failed to load '%s' CrOS component with error: %d",
component_name.c_str(), static_cast<int>(error));
return DemoSetupError(ErrorCode::kOnlineComponentError,
RecoveryMethod::kCheckNetwork, debug_message);
}
DemoSetupController::DemoSetupError::DemoSetupError(
DemoSetupError::ErrorCode error_code,
DemoSetupError::RecoveryMethod recovery_method)
: error_code_(error_code), recovery_method_(recovery_method) {}
DemoSetupController::DemoSetupError::DemoSetupError(
DemoSetupError::ErrorCode error_code,
DemoSetupError::RecoveryMethod recovery_method,
const std::string& debug_message)
: error_code_(error_code),
recovery_method_(recovery_method),
debug_message_(debug_message) {}
DemoSetupController::DemoSetupError::~DemoSetupError() = default;
std::u16string DemoSetupController::DemoSetupError::GetLocalizedErrorMessage()
const {
switch (error_code_) {
case ErrorCode::kOnlineFRECheckRequired:
return l10n_util::GetStringUTF16(
IDS_DEMO_SETUP_OFFLINE_UNAVAILABLE_ERROR);
case ErrorCode::kOnlineComponentError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_COMPONENT_ERROR);
case ErrorCode::kNoStateKeys:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_NO_STATE_KEYS_ERROR);
case ErrorCode::kInvalidRequest:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_INVALID_REQUEST_ERROR);
case ErrorCode::kRequestNetworkError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_NETWORK_ERROR);
case ErrorCode::kTemporaryUnavailable:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_TEMPORARY_ERROR);
case ErrorCode::kResponseError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RESPONSE_ERROR);
case ErrorCode::kResponseDecodingError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RESPONSE_DECODING_ERROR);
case ErrorCode::kDemoAccountError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ACCOUNT_ERROR);
case ErrorCode::kDeviceNotFound:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DEVICE_NOT_FOUND_ERROR);
case ErrorCode::kInvalidDMToken:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_INVALID_DM_TOKEN_ERROR);
case ErrorCode::kInvalidSerialNumber:
return l10n_util::GetStringUTF16(
IDS_DEMO_SETUP_INVALID_SERIAL_NUMBER_ERROR);
case ErrorCode::kDeviceIdError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DEVICE_ID_ERROR);
case ErrorCode::kTooManyRequestsError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_TOO_MANY_REQUESTS_ERROR);
case ErrorCode::kLicenseError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_LICENSE_ERROR);
case ErrorCode::kDeviceDeprovisioned:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DEPROVISIONED_ERROR);
case ErrorCode::kDomainMismatch:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DOMAIN_MISMATCH_ERROR);
case ErrorCode::kSigningError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_SIGNING_ERROR);
case ErrorCode::kPolicyNotFound:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_POLICY_NOT_FOUND_ERROR);
case ErrorCode::kArcError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ARC_ERROR);
case ErrorCode::kRobotFetchError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ROBOT_ERROR);
case ErrorCode::kRobotStoreError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ROBOT_STORE_ERROR);
case ErrorCode::kBadMode:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_BAD_MODE_ERROR);
case ErrorCode::kCertFetchError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_CERT_FETCH_ERROR);
case ErrorCode::kPolicyFetchError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_POLICY_FETCH_ERROR);
case ErrorCode::kPolicyValidationError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_POLICY_VALIDATION_ERROR);
case ErrorCode::kLockTimeout:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_LOCK_TIMEOUT_ERROR);
case ErrorCode::kLockError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_LOCK_ERROR);
case ErrorCode::kAlreadyLocked:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ALREADY_LOCKED_ERROR);
case ErrorCode::kOnlineStoreError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_ONLINE_STORE_ERROR);
case ErrorCode::kMachineIdentificationError:
return l10n_util::GetStringUTF16(
IDS_DEMO_SETUP_NO_MACHINE_IDENTIFICATION_ERROR);
case ErrorCode::kDMTokenStoreError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_DM_TOKEN_STORE_ERROR);
case ErrorCode::kUnexpectedError:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_UNEXPECTED_ERROR);
case ErrorCode::kSuccess:
// We don't display the success message. It's for recording the UMA
// metrics only.
return std::u16string();
}
NOTREACHED() << "No localized error message available for demo setup error.";
}
std::u16string
DemoSetupController::DemoSetupError::GetLocalizedRecoveryMessage() const {
switch (recovery_method_) {
case RecoveryMethod::kRetry:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_RETRY);
case RecoveryMethod::kReboot:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_REBOOT);
case RecoveryMethod::kPowerwash:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_POWERWASH);
case RecoveryMethod::kCheckNetwork:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_CHECK_NETWORK);
case RecoveryMethod::kOnlineOnly:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_OFFLINE_FATAL);
case RecoveryMethod::kUnknown:
return l10n_util::GetStringUTF16(IDS_DEMO_SETUP_RECOVERY_FATAL);
}
NOTREACHED()
<< "No localized error message available for demo setup recovery method.";
}
std::string DemoSetupController::DemoSetupError::GetDebugDescription() const {
return base::StringPrintf("DemoSetupError (code: %d, recovery: %d) : %s",
static_cast<int>(error_code_),
static_cast<int>(recovery_method_),
debug_message_.c_str());
}
// static
void DemoSetupController::ClearDemoRequisition() {
if (policy::EnrollmentRequisitionManager::GetDeviceRequisition() ==
policy::EnrollmentRequisitionManager::kDemoRequisition) {
policy::EnrollmentRequisitionManager::SetDeviceRequisition(std::string());
// If device requisition is `kDemoRequisition`, it means the sub
// organization was also set by the demo setup controller, so remove it as
// well.
policy::EnrollmentRequisitionManager::SetSubOrganization(std::string());
}
}
// static
bool DemoSetupController::IsDemoModeAllowed() {
// Demo mode is only allowed on devices that support ARC++.
return arc::IsArcAvailable();
}
// static
bool DemoSetupController::IsOobeDemoSetupFlowInProgress() {
const WizardController* const wizard_controller =
WizardController::default_controller();
return wizard_controller &&
wizard_controller->demo_setup_controller() != nullptr;
}
// static
std::string DemoSetupController::GetSubOrganizationEmail() {
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
DCHECK(command_line);
if (command_line->HasSwitch(switches::kDemoModeEnrollingUsername)) {
std::string customUser =
command_line->GetSwitchValueASCII(switches::kDemoModeEnrollingUsername);
if (!customUser.empty()) {
std::string email = customUser + "@" + policy::kDemoModeDomain;
VLOG(1) << "Enrolling into Demo Mode with user: " << email;
return email;
}
}
const std::string country =
g_browser_process->local_state()->GetString(prefs::kDemoModeCountry);
std::string country_uppercase = base::ToUpperASCII(country);
std::string country_lowercase = base::ToLowerASCII(country);
// Exclude US as it is the default country.
if (base::Contains(demo_mode::kSupportedCountries, country_uppercase)) {
if (chromeos::features::IsCloudGamingDeviceEnabled()) {
return base::StringPrintf("admin-%s-blazey@%s", country_lowercase.c_str(),
policy::kDemoModeDomain);
}
return "admin-" + country_lowercase + "@" + policy::kDemoModeDomain;
}
return std::string();
}
// static
base::Value::Dict DemoSetupController::GetDemoSetupSteps() {
base::Value::Dict setup_steps_dict;
for (auto entry : GetDemoSetupStepsInfo()) {
setup_steps_dict.Set(GetDemoSetupStepString(entry.step), entry.step_index);
}
return setup_steps_dict;
}
// static
std::string DemoSetupController::GetDemoSetupStepString(
const DemoSetupStep step_enum) {
switch (step_enum) {
case DemoSetupStep::kDownloadResources:
return "downloadResources";
case DemoSetupStep::kEnrollment:
return "enrollment";
case DemoSetupStep::kComplete:
return "complete";
}
NOTREACHED();
}
DemoSetupController::DemoSetupController() = default;
DemoSetupController::~DemoSetupController() = default;
void DemoSetupController::SetAndCanonicalizeRetailerName(
const std::string& retailer_name) {
retailer_name_ = ash::demo_mode::CanonicalizeDimension(retailer_name);
}
void DemoSetupController::Enroll(
OnSetupSuccess on_setup_success,
OnSetupError on_setup_error,
const OnSetCurrentSetupStep& set_current_setup_step) {
DCHECK_NE(demo_config_, DemoSession::DemoModeConfig::kNone)
<< "Demo config needs to be explicitly set before calling Enroll()";
DCHECK(!enrollment_launcher_);
set_current_setup_step_ = set_current_setup_step;
on_setup_success_ = std::move(on_setup_success);
on_setup_error_ = std::move(on_setup_error);
VLOG(1) << "Starting demo setup "
<< DemoSession::DemoConfigToString(demo_config_);
SetCurrentSetupStep(DemoSetupStep::kDownloadResources);
PrefService* prefs = g_browser_process->local_state();
prefs->SetString(prefs::kDemoModeRetailerId, retailer_name_);
prefs->SetString(prefs::kDemoModeStoreId, store_number_);
switch (demo_config_) {
case DemoSession::DemoModeConfig::kOnline:
LoadDemoComponents();
return;
case DemoSession::DemoModeConfig::kNone:
case DemoSession::DemoModeConfig::kOfflineDeprecated:
NOTREACHED() << "No valid demo mode config specified";
}
}
void DemoSetupController::LoadDemoComponents() {
VLOG(1) << "Loading demo resources and demo app components";
download_start_time_ = base::TimeTicks::Now();
if (!demo_components_)
demo_components_ = std::make_unique<DemoComponents>(demo_config_);
// Simulate loading demo components completed for unit tests.
if (DBusThreadManager::Get()->IsUsingFakes() &&
!load_real_components_for_test_) {
demo_components_->SetCrOSComponentLoadedForTesting(
base::FilePath(), component_error_for_tests_);
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&DemoSetupController::OnDemoComponentsLoaded,
weak_ptr_factory_.GetWeakPtr()));
return;
}
auto is_growth_campaigns_enabled_in_demo_mode =
features::IsGrowthCampaignsInDemoModeEnabled();
base::OnceClosure load_callback =
base::BindOnce(&DemoSetupController::OnDemoComponentsLoaded,
weak_ptr_factory_.GetWeakPtr());
base::RepeatingClosure barrier_closure =
base::BarrierClosure(is_growth_campaigns_enabled_in_demo_mode ? 3 : 2,
std::move(load_callback));
demo_components_->LoadResourcesComponent(barrier_closure);
demo_components_->LoadAppComponent(barrier_closure);
if (is_growth_campaigns_enabled_in_demo_mode) {
// Growth campaign is enabled in demo mode, also load growth campaigns
// component.
growth::CampaignsManager::Get()->LoadCampaigns(barrier_closure,
/*in_oobe=*/true);
}
}
void DemoSetupController::OnDemoComponentsLoaded() {
DCHECK_EQ(demo_config_, DemoSession::DemoModeConfig::kOnline);
base::TimeDelta download_duration =
base::TimeTicks::Now() - download_start_time_;
base::UmaHistogramLongTimes100(kDemoSetupDownloadDurationHistogram,
download_duration);
SetCurrentSetupStep(DemoSetupStep::kEnrollment);
auto resources_component_error =
demo_components_->resources_component_error().value_or(
component_updater::ComponentManagerAsh::Error::NOT_FOUND);
auto app_component_error = demo_components_->app_component_error().value_or(
component_updater::ComponentManagerAsh::Error::NOT_FOUND);
// We determine it's an initial loading or retry based on the
// `num_setup_retries_` count.
const std::string kDemoSetupComponentlLoadingResultHistogram =
num_setup_retries_ == 0 ? kDemoSetupComponentInitialLoadingResultHistogram
: kDemoSetupComponentLoadingRetryResultHistogram;
if (resources_component_error !=
component_updater::ComponentManagerAsh::Error::NONE) {
// Reporting the corresponding enum based on the app component error.
base::UmaHistogramEnumeration(
kDemoSetupComponentlLoadingResultHistogram,
app_component_error ==
component_updater::ComponentManagerAsh::Error::NONE
? DemoSetupComponentLoadingResult::kAppSuccessResourcesFailure
: DemoSetupComponentLoadingResult::kAppFailureResourcesFailure);
SetupFailed(DemoSetupError::CreateFromComponentError(
resources_component_error,
DemoComponents::kDemoModeResourcesComponentName));
return;
}
if (app_component_error !=
component_updater::ComponentManagerAsh::Error::NONE) {
// There should be no error on the resources component loading if we've got
// to this point. It should've been handled in the previous "if block".
DCHECK(resources_component_error ==
component_updater::ComponentManagerAsh::Error::NONE);
base::UmaHistogramEnumeration(
kDemoSetupComponentlLoadingResultHistogram,
DemoSetupComponentLoadingResult::kAppFailureResourcesSuccess);
SetupFailed(DemoSetupError::CreateFromComponentError(
app_component_error, DemoComponents::kDemoModeAppComponentName));
return;
}
// There should be no error on both the app and the resources components
// loading if we've got to this point. It should've been handled in the
// previous two "if blocks".
base::UmaHistogramEnumeration(
kDemoSetupComponentlLoadingResultHistogram,
DemoSetupComponentLoadingResult::kAppSuccessResourcesSuccess);
VLOG(1) << "Starting online enrollment";
enroll_start_time_ = base::TimeTicks::Now();
DCHECK(policy::EnrollmentRequisitionManager::GetDeviceRequisition().empty());
policy::EnrollmentRequisitionManager::SetDeviceRequisition(
policy::EnrollmentRequisitionManager::kDemoRequisition);
policy::EnrollmentRequisitionManager::SetSubOrganization(
GetSubOrganizationEmail());
policy::EnrollmentConfig config =
policy::EnrollmentConfig::GetDemoModeEnrollmentConfig();
enrollment_launcher_ =
EnrollmentLauncher::Create(this, config, policy::kDemoModeDomain);
enrollment_launcher_->EnrollUsingAttestation();
}
void DemoSetupController::OnAuthError(const GoogleServiceAuthError& error) {
NOTREACHED();
}
void DemoSetupController::OnEnrollmentError(policy::EnrollmentStatus status) {
SetupFailed(DemoSetupError::CreateFromEnrollmentStatus(status));
}
void DemoSetupController::OnOtherError(EnrollmentLauncher::OtherError error) {
SetupFailed(DemoSetupError::CreateFromOtherEnrollmentError(error));
}
void DemoSetupController::OnDeviceEnrolled() {
DCHECK_NE(demo_config_, DemoSession::DemoModeConfig::kNone);
// `enroll_start_time_` is only set for online enrollment.
if (!enroll_start_time_.is_null()) {
base::TimeDelta enroll_duration =
base::TimeTicks::Now() - enroll_start_time_;
base::UmaHistogramLongTimes100(kDemoSetupEnrollDurationHistogram,
enroll_duration);
}
UMA_HISTOGRAM_ENUMERATION(kDemoSetupErrorHistogram, ErrorCode::kSuccess);
VLOG(1) << "Marking device registered";
StartupUtils::MarkDeviceRegistered(
base::BindOnce(&DemoSetupController::OnDeviceRegistered,
weak_ptr_factory_.GetWeakPtr()));
}
void DemoSetupController::OnDeviceAttributeUploadCompleted(bool success) {
NOTREACHED();
}
void DemoSetupController::OnDeviceAttributeUpdatePermission(bool granted) {
NOTREACHED();
}
void DemoSetupController::SetCrOSComponentLoadErrorForTest(
component_updater::ComponentManagerAsh::Error error) {
component_error_for_tests_ = error;
}
void DemoSetupController::EnableLoadRealComponentsForTest() {
load_real_components_for_test_ = true;
}
void DemoSetupController::OnDeviceRegistered() {
VLOG(1) << "Demo mode setup finished successfully.";
if (demo_config_ == DemoSession::DemoModeConfig::kOnline) {
base::TimeDelta loading_duration =
base::TimeTicks::Now() - download_start_time_;
// A similar metric can be found at OOBE.StepCompletionTime.Demo-setup,
// however this is only useful for durations up to three minutes. Demo mode
// setup typically takes longer than this, so we use a LONG_TIMES metric
// here to capture metrics of up to one hour.
base::UmaHistogramLongTimes100(kDemoSetupLoadingDurationHistogram,
loading_duration);
}
base::UmaHistogramCounts100(kDemoSetupNumRetriesHistogram,
num_setup_retries_);
SetCurrentSetupStep(DemoSetupStep::kComplete);
PrefService* prefs = g_browser_process->local_state();
prefs->SetInteger(prefs::kDemoModeConfig, static_cast<int>(demo_config_));
prefs->CommitPendingWrite();
Reset();
if (!on_setup_success_.is_null())
std::move(on_setup_success_).Run();
}
void DemoSetupController::SetCurrentSetupStep(DemoSetupStep current_step) {
if (!set_current_setup_step_.is_null())
set_current_setup_step_.Run(current_step);
}
void DemoSetupController::SetupFailed(const DemoSetupError& error) {
num_setup_retries_++;
Reset();
LOG(ERROR) << error.GetDebugDescription();
if (!on_setup_error_.is_null())
std::move(on_setup_error_).Run(error);
UMA_HISTOGRAM_ENUMERATION(kDemoSetupErrorHistogram, error.error_code());
}
void DemoSetupController::Reset() {
DCHECK_NE(demo_config_, DemoSession::DemoModeConfig::kNone);
// `demo_config_` is not reset here, because it is needed for retrying setup.
enrollment_launcher_.reset();
ClearDemoRequisition();
}
} // namespace ash