Commit 2de24b99 authored by Aga Wronska's avatar Aga Wronska Committed by Commit Bot

demo mode: Show detailed demo mode setup error information.

When during demo mode setup error occurs the message displayed to the
user contains description of the problem and suggested recovery method.
It helps users as well as support and developers.

Bug: 889604
Test: demo setup unit and browser tests.
Change-Id: Ia2ea4b1d9e1b4667ca9dde139d3a4aaba3a58cd0
Reviewed-on: https://chromium-review.googlesource.com/c/1280311Reviewed-by: default avatarMichael Giuffrida <michaelpg@chromium.org>
Reviewed-by: default avatarRahul Chaturvedi <rkc@chromium.org>
Commit-Queue: Aga Wronska <agawronska@chromium.org>
Cr-Commit-Position: refs/heads/master@{#602425}
parent 2fcf3b65
This diff is collapsed.
......@@ -243,6 +243,22 @@ class DemoSetupTest : public LoginManagerTest {
return js_checker().GetBool(query);
}
// Returns whether error message is shown on demo setup error screen and
// contains text consisting of strings identified by |error_message_id| and
// |recovery_message_id|.
bool IsErrorMessageShown(int error_message_id, int recovery_message_id) {
const std::string element_selector =
base::StrCat({ScreenToContentQuery(OobeScreen::SCREEN_OOBE_DEMO_SETUP),
".$.", DialogToStringId(DemoSetupDialog::kError),
".querySelector('div[slot=subtitle]')"});
const std::string query = base::StrCat(
{"!!", element_selector, " && ", element_selector, ".innerHTML == '",
l10n_util::GetStringUTF8(error_message_id), " ",
l10n_util::GetStringUTF8(recovery_message_id), "' && !",
element_selector, ".hidden"});
return js_checker().GetBool(query);
}
void SetPlayStoreTermsForTesting() {
EXPECT_TRUE(
JSExecute("login.ArcTermsOfServiceScreen.setTosForTesting('Test "
......@@ -624,6 +640,9 @@ IN_PROC_BROWSER_TEST_F(DemoSetupTest, OnlineSetupFlowError) {
// needed to be able to check it reliably.
WaitForScreenDialog(OobeScreen::SCREEN_OOBE_DEMO_SETUP,
DemoSetupDialog::kError);
// Default error returned by MockDemoModeOnlineEnrollmentHelperCreator.
EXPECT_TRUE(IsErrorMessageShown(IDS_DEMO_SETUP_TEMPORARY_ERROR,
IDS_DEMO_SETUP_RECOVERY_RETRY));
EXPECT_FALSE(StartupUtils::IsOobeCompleted());
EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
}
......@@ -680,6 +699,8 @@ IN_PROC_BROWSER_TEST_F(DemoSetupTest, OnlineSetupFlowCrosComponentFailure) {
// needed to be able to check it reliably.
WaitForScreenDialog(OobeScreen::SCREEN_OOBE_DEMO_SETUP,
DemoSetupDialog::kError);
EXPECT_TRUE(IsErrorMessageShown(IDS_DEMO_SETUP_COMPONENT_ERROR,
IDS_DEMO_SETUP_RECOVERY_CHECK_NETWORK));
EXPECT_FALSE(StartupUtils::IsOobeCompleted());
EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
}
......@@ -787,6 +808,9 @@ IN_PROC_BROWSER_TEST_F(DemoSetupTest, OfflineSetupFlowError) {
// needed to be able to check it reliably.
WaitForScreenDialog(OobeScreen::SCREEN_OOBE_DEMO_SETUP,
DemoSetupDialog::kError);
// Default error returned by MockDemoModeOfflineEnrollmentHelperCreator.
EXPECT_TRUE(IsErrorMessageShown(IDS_DEMO_SETUP_LOCK_ERROR,
IDS_DEMO_SETUP_RECOVERY_POWERWASH));
EXPECT_FALSE(StartupUtils::IsOobeCompleted());
EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
......
......@@ -11,8 +11,10 @@
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/strings/string16.h"
#include "chrome/browser/chromeos/login/demo_mode/demo_session.h"
#include "chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper.h"
#include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
#include "chrome/browser/component_updater/cros_component_installer_chromeos.h"
#include "components/policy/core/common/cloud/cloud_policy_store.h"
......@@ -29,19 +31,130 @@ class DemoSetupController
: public EnterpriseEnrollmentHelper::EnrollmentStatusConsumer,
public policy::CloudPolicyStore::Observer {
public:
// Type of demo mode setup error.
enum class DemoSetupError {
// Recoverable or temporary demo mode setup error. Another attempt to setup
// demo mode may succeed.
kRecoverable,
// Fatal demo mode setup error. Device requires powerwash to recover from
// the resulting state.
kFatal,
// Contains information related to setup error.
class DemoSetupError {
public:
// Type of setup error.
enum class ErrorCode {
// Offline resources not available on device.
kNoOfflineResources,
// Cannot load or parse offline policy.
kOfflinePolicyError,
// Local account policy store error.
kOfflinePolicyStoreError,
// Cannot perform offline setup without online FRE check.
kOnlineFRECheckRequired,
// Cannot load online component.
kOnlineComponentError,
// Invalid request to DMServer.
kInvalidRequest,
// Request to DMServer failed, because of network error.
kRequestNetworkError,
// DMServer temporary unavailable.
kTemporaryUnavailable,
// DMServer returned abnormal response code.
kResponseError,
// DMServer response cannot be decoded.
kResponseDecodingError,
// Device management not supported for demo account.
kDemoAccountError,
// DMServer cannot find the device.
kDeviceNotFound,
// Invalid device management token.
kInvalidDMToken,
// Serial number invalid or unknown to DMServer,
kInvalidSerialNumber,
// Device id conflict.
kDeviceIdError,
// Not enough licenses or domain expired.
kLicenseError,
// Device was deprovisioned.ec
kDeviceDeprovisioned,
// Device belongs to different domain (FRE).
kDomainMismatch,
// Management request could not be signed by the client.
kSigningError,
// DMServer could not find policy for the device.
kPolicyNotFound,
// ARC disabled for demo domain.
kArcError,
// Cannot determine server-backed state keys.
kNoStateKeys,
// Failed to fetch robot account auth or refresh token.
kRobotFetchError,
// Failed to fetch robot account refresh token.
kRobotStoreError,
// Unsuppored device mode returned by the server.
kBadMode,
// Could not fetch registration cert,
kCertFetchError,
// Could not fetch the policy.
kPolicyFetchError,
// Policy validation failed.
kPolicyValidationError,
// Timeout during locking the device.
kLockTimeout,
// Error during locking the device.
kLockError,
// Device locked to different domain on mode.
kAlreadyLocked,
// Error while installing online policy.
kOnlineStoreError,
// Could not determine device model or serial number.
kMachineIdentificationError,
// Could not store DM token.
kDMTokenStoreError,
// Unexpected/fatal error.
kUnexpectedError,
};
// Type of recommended recovery from the setup error.
enum class RecoveryMethod {
// Retry demo setup.
kRetry,
// Reboot and retry demo setup.
kReboot,
// Powerwash and retry demo setup.
kPowerwash,
// Check network and retry demo setup.
kCheckNetwork,
// Cannot perform offline setup - online setup might work.
kOnlineOnly,
// Unknown recovery method.
kUnknown,
};
static DemoSetupError CreateFromEnrollmentStatus(
const policy::EnrollmentStatus& status);
static DemoSetupError CreateFromOtherEnrollmentError(
EnterpriseEnrollmentHelper::OtherError error);
static DemoSetupError CreateFromComponentError(
component_updater::CrOSComponentManager::Error error);
DemoSetupError(ErrorCode error_code, RecoveryMethod recovery_method);
DemoSetupError(ErrorCode error_code,
RecoveryMethod recovery_method,
const std::string& debug_message);
~DemoSetupError();
ErrorCode error_code() const { return error_code_; }
RecoveryMethod recovery_method() const { return recovery_method_; }
base::string16 GetLocalizedErrorMessage() const;
base::string16 GetLocalizedRecoveryMessage() const;
std::string GetDebugDescription() const;
private:
ErrorCode error_code_;
RecoveryMethod recovery_method_;
std::string debug_message_;
};
// Demo mode setup callbacks.
using OnSetupSuccess = base::OnceClosure;
using OnSetupError = base::OnceCallback<void(DemoSetupError)>;
using OnSetupError = base::OnceCallback<void(const DemoSetupError&)>;
// Domain that demo mode devices are enrolled into.
static constexpr char kDemoModeDomain[] = "cros-demo-mode.com";
......@@ -129,8 +242,8 @@ class DemoSetupController
// is completed. This is the last step of demo mode setup flow.
void OnDeviceRegistered();
// Finish the flow with an error message.
void SetupFailed(const std::string& message, DemoSetupError error);
// Finish the flow with an error.
void SetupFailed(const DemoSetupError& error);
// Clears the internal state.
void Reset();
......
......@@ -46,7 +46,7 @@ class DemoSetupControllerTestHelper {
: run_loop_(std::make_unique<base::RunLoop>()) {}
virtual ~DemoSetupControllerTestHelper() = default;
void OnSetupError(DemoSetupController::DemoSetupError error) {
void OnSetupError(const DemoSetupController::DemoSetupError& error) {
EXPECT_FALSE(succeeded_.has_value());
succeeded_ = false;
error_ = error;
......@@ -67,9 +67,11 @@ class DemoSetupControllerTestHelper {
return succeeded_.has_value() && succeeded_.value() == expected;
}
// Returns true if it receives a fatal error.
bool IsErrorFatal() const {
return error_ == DemoSetupController::DemoSetupError::kFatal;
// Returns true if powerwash is required to recover from the error.
bool RequiresPowerwash() const {
return error_.has_value() &&
error_->recovery_method() ==
DemoSetupController::DemoSetupError::RecoveryMethod::kPowerwash;
}
void Reset() {
......@@ -79,8 +81,7 @@ class DemoSetupControllerTestHelper {
private:
base::Optional<bool> succeeded_;
DemoSetupController::DemoSetupError error_ =
DemoSetupController::DemoSetupError::kRecoverable;
base::Optional<DemoSetupController::DemoSetupError> error_;
std::unique_ptr<base::RunLoop> run_loop_;
DISALLOW_COPY_AND_ASSIGN(DemoSetupControllerTestHelper);
......@@ -177,7 +178,7 @@ TEST_F(DemoSetupControllerTest, OfflineDeviceLocalAccountPolicyLoadFailure) {
base::Unretained(helper_.get())));
EXPECT_TRUE(helper_->WaitResult(false));
EXPECT_FALSE(helper_->IsErrorFatal());
EXPECT_FALSE(helper_->RequiresPowerwash());
EXPECT_EQ("", GetDeviceRequisition());
}
......@@ -203,7 +204,7 @@ TEST_F(DemoSetupControllerTest, OfflineDeviceLocalAccountPolicyStoreFailed) {
base::Unretained(helper_.get())));
EXPECT_TRUE(helper_->WaitResult(false));
EXPECT_TRUE(helper_->IsErrorFatal());
EXPECT_TRUE(helper_->RequiresPowerwash());
EXPECT_EQ("", GetDeviceRequisition());
}
......@@ -224,7 +225,7 @@ TEST_F(DemoSetupControllerTest, OfflineInvalidDeviceLocalAccountPolicyBlob) {
base::Unretained(helper_.get())));
EXPECT_TRUE(helper_->WaitResult(false));
EXPECT_TRUE(helper_->IsErrorFatal());
EXPECT_TRUE(helper_->RequiresPowerwash());
EXPECT_EQ("", GetDeviceRequisition());
}
......@@ -248,7 +249,7 @@ TEST_F(DemoSetupControllerTest, OfflineError) {
base::Unretained(helper_.get())));
EXPECT_TRUE(helper_->WaitResult(false));
EXPECT_FALSE(helper_->IsErrorFatal());
EXPECT_TRUE(helper_->RequiresPowerwash());
EXPECT_EQ("", GetDeviceRequisition());
}
......@@ -279,7 +280,7 @@ TEST_F(DemoSetupControllerTest, OnlineError) {
base::Unretained(helper_.get())));
EXPECT_TRUE(helper_->WaitResult(false));
EXPECT_FALSE(helper_->IsErrorFatal());
EXPECT_FALSE(helper_->RequiresPowerwash());
EXPECT_EQ("", GetDeviceRequisition());
}
......@@ -298,7 +299,7 @@ TEST_F(DemoSetupControllerTest, OnlineComponentError) {
base::Unretained(helper_.get())));
EXPECT_TRUE(helper_->WaitResult(false));
EXPECT_FALSE(helper_->IsErrorFatal());
EXPECT_FALSE(helper_->RequiresPowerwash());
EXPECT_EQ("", GetDeviceRequisition());
}
......@@ -314,7 +315,7 @@ TEST_F(DemoSetupControllerTest, EnrollTwice) {
base::Unretained(helper_.get())));
EXPECT_TRUE(helper_->WaitResult(false));
EXPECT_FALSE(helper_->IsErrorFatal());
EXPECT_FALSE(helper_->RequiresPowerwash());
EXPECT_EQ("", GetDeviceRequisition());
helper_->Reset();
......
......@@ -12,6 +12,8 @@
#include "chrome/browser/chromeos/login/enrollment/enterprise_enrollment_helper_mock.h"
#include "chrome/browser/chromeos/policy/enrollment_config.h"
#include "chrome/browser/chromeos/policy/enrollment_status_chromeos.h"
#include "chromeos/settings/install_attributes.h"
#include "components/policy/core/common/cloud/cloud_policy_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
......@@ -40,8 +42,9 @@ EnterpriseEnrollmentHelper* MockDemoModeOnlineEnrollmentHelperCreator(
} else {
// TODO(agawronska): Test different error types.
mock->status_consumer()->OnEnrollmentError(
policy::EnrollmentStatus::ForStatus(
policy::EnrollmentStatus::REGISTRATION_FAILED));
policy::EnrollmentStatus::ForRegistrationError(
policy::DeviceManagementStatus::
DM_STATUS_TEMPORARY_UNAVAILABLE));
}
}));
return mock;
......@@ -66,8 +69,8 @@ EnterpriseEnrollmentHelper* MockDemoModeOfflineEnrollmentHelperCreator(
} else {
// TODO(agawronska): Test different error types.
mock->status_consumer()->OnEnrollmentError(
policy::EnrollmentStatus::ForStatus(
policy::EnrollmentStatus::Status::LOCK_ERROR));
policy::EnrollmentStatus::ForLockError(
chromeos::InstallAttributes::LOCK_READBACK_ERROR));
}
}));
return mock;
......
......@@ -52,10 +52,6 @@ void DemoSetupScreen::OnUserAction(const std::string& action_id) {
}
}
void DemoSetupScreen::OnSetupError(DemoSetupController::DemoSetupError error) {
// TODO(mukai): propagate |error| information and change the error message.
view_->OnSetupFinished(false, std::string());
}
void DemoSetupScreen::StartEnrollment() {
// Demo setup screen is only shown in OOBE.
......@@ -68,6 +64,11 @@ void DemoSetupScreen::StartEnrollment() {
weak_ptr_factory_.GetWeakPtr()));
}
void DemoSetupScreen::OnSetupError(
const DemoSetupController::DemoSetupError& error) {
view_->OnSetupFailed(error);
}
void DemoSetupScreen::OnSetupSuccess() {
Finish(ScreenExitCode::DEMO_MODE_SETUP_FINISHED);
}
......
......@@ -38,7 +38,7 @@ class DemoSetupScreen : public BaseScreen {
void StartEnrollment();
// Called when the setup flow finished with error.
void OnSetupError(DemoSetupController::DemoSetupError error);
void OnSetupError(const DemoSetupController::DemoSetupError& error);
// Called when the setup flow finished successfully.
void OnSetupSuccess();
......
......@@ -7,6 +7,7 @@
#include <string>
#include "chrome/browser/chromeos/login/demo_mode/demo_setup_controller.h"
#include "chrome/browser/chromeos/login/oobe_screen.h"
namespace chromeos {
......@@ -29,8 +30,12 @@ class DemoSetupScreenView {
// Sets view and screen.
virtual void Bind(DemoSetupScreen* screen) = 0;
// Handles setup result.
virtual void OnSetupFinished(bool is_success, const std::string& message) = 0;
// Handles successful setup.
virtual void OnSetupSucceeded() = 0;
// Handles setup failure.
virtual void OnSetupFailed(
const DemoSetupController::DemoSetupError& error) = 0;
};
} // namespace chromeos
......
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_MOCK_DEMO_SETUP_SCREEN_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENS_MOCK_DEMO_SETUP_SCREEN_H_
#include "chrome/browser/chromeos/login/demo_mode/demo_setup_controller.h"
#include "chrome/browser/chromeos/login/screens/demo_setup_screen.h"
#include "chrome/browser/chromeos/login/screens/demo_setup_screen_view.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -26,8 +27,9 @@ class MockDemoSetupScreenView : public DemoSetupScreenView {
MOCK_METHOD0(Show, void());
MOCK_METHOD0(Hide, void());
MOCK_METHOD1(MockBind, void(DemoSetupScreen* screen));
MOCK_METHOD2(OnSetupFinished,
void(bool is_success, const std::string& message));
MOCK_METHOD0(OnSetupSucceeded, void());
MOCK_METHOD1(OnSetupFailed,
void(const DemoSetupController::DemoSetupError& error));
void Bind(DemoSetupScreen* screen) override;
......
......@@ -45,9 +45,7 @@
icon1x="demo-setup-32:computer" icon2x="demo-setup-64:computer">
</hd-iron-icon>
<h1 slot="title">[[i18nDynamic(locale, 'demoSetupErrorScreenTitle')]]</h1>
<div slot="subtitle">
[[i18nDynamic(locale, 'demoSetupErrorScreenSubtitle')]]
</div>
<div slot="subtitle">[[errorMessage_]]</div>
<div slot="footer" class="flex layout vertical center center-justified">
<img srcset="images/alert-illustration_1x.svg 1x,
images/alert-illustration_2x.svg 2x">
......
......@@ -13,6 +13,12 @@ Polymer({
behaviors: [I18nBehavior, OobeDialogHostBehavior],
properties: {
/** Error message displayed on demoSetupErrorDialog screen. */
errorMessage_: {
type: String,
value: '',
},
/** Ordered array of screen ids that are a part of demo setup flow. */
screens_: {
type: Array,
......@@ -34,16 +40,18 @@ Polymer({
this.i18nUpdateLocale();
},
/** Called when demo mode setup succeeded. */
onSetupSucceeded: function() {
this.errorMessage_ = '';
},
/**
* Called when demo mode setup finished.
* @param {boolean} isSuccess Whether demo setup finished successfully.
* @param {string} message Error message to be displayed to the user if setup
* finished with an error.
* Called when demo mode setup failed.
* @param {string} message Error message to be displayed to the user.
*/
onSetupFinished: function(isSuccess, message) {
if (!isSuccess) {
this.showScreen_('demoSetupErrorDialog');
}
onSetupFailed: function(message) {
this.errorMessage_ = message;
this.showScreen_('demoSetupErrorDialog');
},
/**
......
......@@ -8,7 +8,7 @@
login.createScreen('DemoSetupScreen', 'demo-setup', function() {
return {
EXTERNAL_API: ['onSetupFinished'],
EXTERNAL_API: ['onSetupSucceeded', 'onSetupFailed'],
/**
* Demo setup module.
......@@ -37,14 +37,17 @@ login.createScreen('DemoSetupScreen', 'demo-setup', function() {
this.demoSetupModule_.reset();
},
/** Called when demo mode setup succeeded. */
onSetupSucceeded: function() {
this.demoSetupModule_.onSetupSucceeded();
},
/**
* Called when demo mode setup finished.
* @param {boolean} isSuccess Whether demo setup finished successfully.
* @param {string} message Error message to be displayed to the user,
* populated if setup finished with an error.
* Called when demo mode setup failed.
* @param {string} message Error message to be displayed to the user.
*/
onSetupFinished: function(isSuccess, message) {
this.demoSetupModule_.onSetupFinished(isSuccess, message);
onSetupFailed: function(message) {
this.demoSetupModule_.onSetupFailed(message);
},
};
});
......@@ -4,6 +4,9 @@
#include "chrome/browser/ui/webui/chromeos/login/demo_setup_screen_handler.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/login/oobe_screen.h"
#include "chrome/browser/chromeos/login/screens/demo_setup_screen.h"
#include "chrome/grit/generated_resources.h"
......@@ -38,9 +41,16 @@ void DemoSetupScreenHandler::Bind(DemoSetupScreen* screen) {
BaseScreenHandler::SetBaseScreen(screen);
}
void DemoSetupScreenHandler::OnSetupFinished(bool is_success,
const std::string& message) {
CallJS("onSetupFinished", is_success, message);
void DemoSetupScreenHandler::OnSetupFailed(
const DemoSetupController::DemoSetupError& error) {
CallJS("onSetupFailed",
base::JoinString({error.GetLocalizedErrorMessage(),
error.GetLocalizedRecoveryMessage()},
base::UTF8ToUTF16(" ")));
}
void DemoSetupScreenHandler::OnSetupSucceeded() {
CallJS("onSetupSucceeded");
}
void DemoSetupScreenHandler::Initialize() {}
......@@ -51,8 +61,6 @@ void DemoSetupScreenHandler::DeclareLocalizedValues(
IDS_OOBE_DEMO_SETUP_PROGRESS_SCREEN_TITLE);
builder->Add("demoSetupErrorScreenTitle",
IDS_OOBE_DEMO_SETUP_ERROR_SCREEN_TITLE);
builder->Add("demoSetupErrorScreenSubtitle",
IDS_OOBE_DEMO_SETUP_ERROR_SCREEN_SUBTITLE);
builder->Add("demoSetupErrorScreenRetryButtonLabel",
IDS_OOBE_DEMO_SETUP_ERROR_SCREEN_RETRY_BUTTON_LABEL);
}
......
......@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_DEMO_SETUP_SCREEN_HANDLER_H_
#define CHROME_BROWSER_UI_WEBUI_CHROMEOS_LOGIN_DEMO_SETUP_SCREEN_HANDLER_H_
#include "chrome/browser/chromeos/login/demo_mode/demo_setup_controller.h"
#include "chrome/browser/chromeos/login/screens/demo_setup_screen_view.h"
#include "chrome/browser/ui/webui/chromeos/login/base_screen_handler.h"
......@@ -24,7 +25,8 @@ class DemoSetupScreenHandler : public BaseScreenHandler,
void Show() override;
void Hide() override;
void Bind(DemoSetupScreen* screen) override;
void OnSetupFinished(bool is_success, const std::string& message) override;
void OnSetupFailed(const DemoSetupController::DemoSetupError& error) override;
void OnSetupSucceeded() override;
// BaseScreenHandler:
void Initialize() override;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment