Commit 7d8cd352 authored by Michael Giuffrida's avatar Michael Giuffrida Committed by Commit Bot

Enroll device into Demo Mode when derelict

When a device is "derelict" (idle at OOBE), we currently auto-launch a
very old Kiosk demo app. This CL changes the idle detector to enroll
into offline Demo Mode instead if Demo Mode and offline Demo Mode are
enabled.

More specifically, "derelict" is defined as:
* Spending a total of 8 hours on the Welcome screen, followed by
* Being idle on the Welcome screen for 5 minutes

So a device that has progressed past the Welcome screen shouldn't be
considered derelict; nor should a device that goes idle for 5 minutes
but has only been in OOBE for <8 hours.

We will follow this CL  with actual derelict policies; until then it
will fail to enroll and simply return to the Welcome screen.

Bug: 869273
Test: browser_tests, and manually at OOBE with the following command line:
      --enable-demo-mode --enable-offline-demo-mode --derelict-idle-timeout=1
      --derelict-detection-timeout=1 --oobe-timer-interval=1
      (manual testing requires copying policies from
      /usr/share/chromeos-assets/demo_mode_resources/policy/ to
      /usr/share/chromeos-assets/demo_mode_resources/derelict_policy/)

Change-Id: I81572e447ce5082c0bf534ab32b0c4b34f0570a0
Reviewed-on: https://chromium-review.googlesource.com/1164398
Commit-Queue: Michael Giuffrida <michaelpg@chromium.org>
Reviewed-by: default avatarAlexander Alekseev <alemate@chromium.org>
Reviewed-by: default avatarAga Wronska <agawronska@chromium.org>
Cr-Commit-Position: refs/heads/master@{#583927}
parent 984bdebb
......@@ -9,6 +9,7 @@
#include "base/sys_info.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/wizard_controller.h"
#include "chrome/common/pref_names.h"
#include "chromeos/chromeos_switches.h"
#include "components/prefs/pref_registry_simple.h"
......@@ -83,7 +84,15 @@ void DemoModeDetector::OnIdle() {
if (demo_launched_)
return;
demo_launched_ = true;
LoginDisplayHost::default_host()->StartDemoAppLaunch();
// Offline demo mode can be only enabled when demo mode feature is enabled.
const auto* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kEnableDemoMode) &&
command_line->HasSwitch(switches::kEnableOfflineDemoMode)) {
WizardController::default_controller()->StartDerelictDemoModeSetup();
} else {
LoginDisplayHost::default_host()->StartDemoAppLaunch();
}
}
void DemoModeDetector::OnOobeTimerUpdate() {
......
......@@ -18,8 +18,9 @@ class PrefRegistrySimple;
namespace chromeos {
// Helper for idle state and demo-mode detection.
// Should be initialized on OOBE start.
// Helper for idle state and demo-mode detection. Will either enroll the device
// into derelict offline Demo Mode or launch the old demo app in Kiosk mode,
// depending on offline demo mode availability.
class DemoModeDetector {
public:
DemoModeDetector();
......
......@@ -31,9 +31,11 @@ constexpr char kOfflineDeviceLocalAccountPolicyFileName[] =
"local_account_policy";
// The policy blob data for offline demo-mode is embedded into the filesystem.
// TODO(mukai, agawronska): fix this when switching to dm-verity image.
// TODO(mukai, agawronska): fix these when switching to dm-verity image.
constexpr const base::FilePath::CharType kOfflineDemoModeDir[] =
FILE_PATH_LITERAL("/usr/share/demo_mode_resources/policy");
constexpr const base::FilePath::CharType kDerelictOfflineDemoModeDir[] =
FILE_PATH_LITERAL("/usr/share/demo_mode_resources/derelict_policy");
bool CheckOfflinePolicyFilesExist(const base::FilePath& policy_dir,
std::string* message) {
......@@ -130,7 +132,12 @@ DemoSetupController::~DemoSetupController() {
}
bool DemoSetupController::IsOfflineEnrollment() const {
return enrollment_type_ && *enrollment_type_ == EnrollmentType::kOffline;
return enrollment_type_ && *enrollment_type_ != EnrollmentType::kOnline;
}
bool DemoSetupController::IsDerelictOfflineEnrollment() const {
return enrollment_type_ &&
*enrollment_type_ == EnrollmentType::kDerelictOffline;
}
void DemoSetupController::Enroll(OnSetupSuccess on_setup_success,
......@@ -143,20 +150,21 @@ void DemoSetupController::Enroll(OnSetupSuccess on_setup_success,
on_setup_success_ = std::move(on_setup_success);
on_setup_error_ = std::move(on_setup_error);
switch (*enrollment_type_) {
case EnrollmentType::kOnline:
EnrollOnline();
return;
case EnrollmentType::kOffline: {
const base::FilePath offline_data_dir =
policy_dir_for_tests_.empty() ? base::FilePath(kOfflineDemoModeDir)
: policy_dir_for_tests_;
EnrollOffline(offline_data_dir);
return;
}
default:
NOTREACHED() << "Unknown demo mode enrollment type";
if (*enrollment_type_ == EnrollmentType::kOnline) {
EnrollOnline();
return;
}
base::FilePath offline_data_dir;
if (!policy_dir_for_tests_.empty()) {
offline_data_dir = policy_dir_for_tests_;
} else {
offline_data_dir =
base::FilePath(*enrollment_type_ == EnrollmentType::kOffline
? kOfflineDemoModeDir
: kDerelictOfflineDemoModeDir);
}
EnrollOffline(offline_data_dir);
}
void DemoSetupController::EnrollOnline() {
......
......@@ -29,6 +29,9 @@ class DemoSetupController
kOnline,
// Offline enrollment into demo mode that is established locally.
kOffline,
// Offline enrollment into demo mode that is established locally due to
// device being derelict.
kDerelictOffline,
};
// Type of demo mode setup error.
......@@ -68,9 +71,13 @@ class DemoSetupController
enrollment_type_ = enrollment_type;
}
// Whether offline enrollment is used for setup.
// Whether offline enrollment is used for setup. This includes manual and
// derelict enrollment.
bool IsOfflineEnrollment() const;
// Whether derelict offline enrollment occurred.
bool IsDerelictOfflineEnrollment() const;
// Initiates enrollment that sets up the device in the demo mode domain. The
// |enrollment_type_| determines whether online or offline setup will be
// performed and it should be set with set_enrollment_type() before calling
......
......@@ -382,20 +382,26 @@ class DemoSetupTest : public LoginManagerTest {
OobeScreen::SCREEN_OOBE_DEMO_SETUP));
}
void SimulateOfflineEnvironment() {
// Simulates offline data directory.
void MockOfflinePolicyDir() {
DemoSetupController* controller =
WizardController::default_controller()->demo_setup_controller();
// Simulate offline data directory.
ASSERT_TRUE(SetupDummyOfflinePolicyDir("test", &fake_policy_dir_));
controller->SetOfflineDataDirForTest(fake_policy_dir_.GetPath());
}
// Simulates policy store that loads or fails to load.
void MockOfflinePolicyStore(bool valid) {
// Simulate policy store.
EXPECT_CALL(mock_policy_store_, Store(testing::_))
.WillRepeatedly(testing::InvokeWithoutArgs(
&mock_policy_store_,
&policy::MockCloudPolicyStore::NotifyStoreLoaded));
controller->SetDeviceLocalAccountPolicyStoreForTest(&mock_policy_store_);
valid ? &policy::MockCloudPolicyStore::NotifyStoreLoaded
: &policy::MockCloudPolicyStore::NotifyStoreError));
WizardController::default_controller()
->demo_setup_controller()
->SetDeviceLocalAccountPolicyStoreForTest(&mock_policy_store_);
}
// Simulates device being connected to the network.
......@@ -629,7 +635,8 @@ IN_PROC_BROWSER_TEST_F(DemoSetupTest, OfflineSetupFlowSuccess) {
// It needs to be done after demo setup controller was created (demo setup
// flow was started).
SimulateOfflineEnvironment();
MockOfflinePolicyDir();
MockOfflinePolicyStore(true /* valid */);
OobeScreenWaiter(OobeScreen::SCREEN_OOBE_DEMO_PREFERENCES).Wait();
EXPECT_TRUE(IsScreenShown(OobeScreen::SCREEN_OOBE_DEMO_PREFERENCES));
......@@ -673,7 +680,8 @@ IN_PROC_BROWSER_TEST_F(DemoSetupTest, OfflineSetupFlowError) {
// It needs to be done after demo setup controller was created (demo setup
// flow was started).
SimulateOfflineEnvironment();
MockOfflinePolicyDir();
MockOfflinePolicyStore(true /* valid */);
OobeScreenWaiter(OobeScreen::SCREEN_OOBE_DEMO_PREFERENCES).Wait();
EXPECT_TRUE(IsScreenShown(OobeScreen::SCREEN_OOBE_DEMO_PREFERENCES));
......@@ -707,6 +715,87 @@ IN_PROC_BROWSER_TEST_F(DemoSetupTest, OfflineSetupFlowError) {
EXPECT_FALSE(StartupUtils::IsDeviceRegistered());
}
IN_PROC_BROWSER_TEST_F(DemoSetupTest, DerelictOfflineSetupFlowSuccess) {
// Simulate offline setup success.
EnterpriseEnrollmentHelper::SetupEnrollmentHelperMock(
&MockDemoModeOfflineEnrollmentHelperCreator<
DemoModeSetupResult::SUCCESS>);
SimulateNetworkDisconnected();
auto* const wizard_controller = WizardController::default_controller();
wizard_controller->StartDerelictDemoModeSetup();
EXPECT_TRUE(
wizard_controller->demo_setup_controller()->IsOfflineEnrollment());
EXPECT_TRUE(wizard_controller->demo_setup_controller()
->IsDerelictOfflineEnrollment());
// This needs to be done after demo setup controller was created (demo setup
// flow was started).
MockOfflinePolicyDir();
MockOfflinePolicyStore(true /* valid */);
OobeScreenWaiter(OobeScreen::SCREEN_OOBE_DEMO_SETUP).Wait();
// TODO(michaelpg): Progress dialog transition is async - extra work is
// needed to be able to check it reliably.
OobeScreenWaiter(OobeScreen::SCREEN_GAIA_SIGNIN).Wait();
}
IN_PROC_BROWSER_TEST_F(DemoSetupTest, DerelictOfflineSetupFlowError) {
// Simulate offline setup error.
EnterpriseEnrollmentHelper::SetupEnrollmentHelperMock(
&MockDemoModeOfflineEnrollmentHelperCreator<DemoModeSetupResult::ERROR>);
SimulateNetworkDisconnected();
auto* const wizard_controller = WizardController::default_controller();
wizard_controller->StartDerelictDemoModeSetup();
EXPECT_TRUE(
wizard_controller->demo_setup_controller()->IsOfflineEnrollment());
EXPECT_TRUE(wizard_controller->demo_setup_controller()
->IsDerelictOfflineEnrollment());
// This needs to be done after demo setup controller was created (demo setup
// flow was started).
MockOfflinePolicyDir();
MockOfflinePolicyStore(true /* valid */);
OobeScreenWaiter(OobeScreen::SCREEN_OOBE_DEMO_SETUP).Wait();
// For derelict enrollment, a recoverable error returns the user to the
// Welcome screen.
OobeScreenWaiter(OobeScreen::SCREEN_OOBE_WELCOME).Wait();
}
IN_PROC_BROWSER_TEST_F(DemoSetupTest, DerelictOfflineSetupFlowFatalError) {
// Simulate offline setup success. The fatal error will come from the
// unreadable policy store.
EnterpriseEnrollmentHelper::SetupEnrollmentHelperMock(
&MockDemoModeOfflineEnrollmentHelperCreator<
DemoModeSetupResult::SUCCESS>);
SimulateNetworkDisconnected();
auto* const wizard_controller = WizardController::default_controller();
wizard_controller->StartDerelictDemoModeSetup();
EXPECT_TRUE(
wizard_controller->demo_setup_controller()->IsOfflineEnrollment());
EXPECT_TRUE(wizard_controller->demo_setup_controller()
->IsDerelictOfflineEnrollment());
// This needs to be done after demo setup controller was created (demo setup
// flow was started).
MockOfflinePolicyDir();
MockOfflinePolicyStore(false /* valid */);
// On fatal error, stay on the Setup screen.
OobeScreenWaiter(OobeScreen::SCREEN_OOBE_DEMO_SETUP).Wait();
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(IsScreenShown(OobeScreen::SCREEN_OOBE_DEMO_SETUP));
}
IN_PROC_BROWSER_TEST_F(DemoSetupTest, NextDisabledOnNetworkScreen) {
SimulateNetworkDisconnected();
SkipToScreen(OobeScreen::SCREEN_OOBE_NETWORK);
......@@ -858,4 +947,37 @@ IN_PROC_BROWSER_TEST_F(DemoSetupArcUnsupportedTest, DoNotInvokeWithTaps) {
EXPECT_FALSE(IsConfirmationDialogShown());
}
class DemoSetupDerelictTest : public DemoSetupTest {
protected:
DemoSetupDerelictTest() = default;
~DemoSetupDerelictTest() override = default;
// DemoSetupTest:
void SetUpCommandLine(base::CommandLine* command_line) override {
DemoSetupTest::SetUpCommandLine(command_line);
command_line->AppendSwitchASCII(switches::kDerelictDetectionTimeout, "1");
command_line->AppendSwitchASCII(switches::kDerelictIdleTimeout, "1");
command_line->AppendSwitchASCII(switches::kOobeTimerInterval, "1");
}
private:
DISALLOW_COPY_AND_ASSIGN(DemoSetupDerelictTest);
};
IN_PROC_BROWSER_TEST_F(DemoSetupDerelictTest, DerelictSetup) {
// Simulate offline setup error.
EnterpriseEnrollmentHelper::SetupEnrollmentHelperMock(
&MockDemoModeOfflineEnrollmentHelperCreator<DemoModeSetupResult::ERROR>);
SimulateNetworkDisconnected();
// Demo setup starts automatically after idle detection timeouts.
OobeScreenWaiter(OobeScreen::SCREEN_OOBE_DEMO_SETUP).Wait();
// TODO(michaelpg): Mock policy directory and policy store so we can test
// enrollment success. Currently this must be done after the
// DemoSetupController is created, but policy install has already failed by
// this point.
OobeScreenWaiter(OobeScreen::SCREEN_OOBE_WELCOME).Wait();
}
} // namespace chromeos
......@@ -33,6 +33,7 @@ DemoSetupScreen::~DemoSetupScreen() {
}
void DemoSetupScreen::Show() {
DCHECK(DemoSetupController::IsOobeDemoSetupFlowInProgress());
if (view_)
view_->Show();
}
......@@ -53,6 +54,16 @@ void DemoSetupScreen::OnUserAction(const std::string& action_id) {
}
void DemoSetupScreen::OnSetupError(DemoSetupController::DemoSetupError error) {
if (error != DemoSetupController::DemoSetupError::kFatal &&
WizardController::default_controller()
->demo_setup_controller()
->IsDerelictOfflineEnrollment()) {
// Silently return to the welcome screen since enrollment was attempted
// automatically.
Finish(ScreenExitCode::DEMO_MODE_SETUP_CANCELED);
return;
}
// TODO(mukai): propagate |error| information and change the error message.
view_->OnSetupFinished(false, std::string());
}
......
......@@ -1436,6 +1436,14 @@ void WizardController::StartDemoModeSetup() {
ShowDemoModePreferencesScreen();
}
void WizardController::StartDerelictDemoModeSetup() {
DCHECK(!demo_setup_controller_);
demo_setup_controller_ = std::make_unique<DemoSetupController>();
demo_setup_controller_->set_enrollment_type(
DemoSetupController::EnrollmentType::kDerelictOffline);
ShowDemoModeSetupScreen();
}
void WizardController::SimulateDemoModeSetupForTesting() {
demo_setup_controller_ = std::make_unique<DemoSetupController>();
}
......
......@@ -114,6 +114,12 @@ class WizardController : public BaseScreenDelegate,
// chromeos::OobeScreen::SCREEN_OOBE_DEMO_SETUP
void StartDemoModeSetup();
// Starts derelict Demo Mode setup flow. This should only occur when the
// device has been idle on the Welcome screen.
// The device will auto-enroll into derelict Demo Mode with no user input
// required.
void StartDerelictDemoModeSetup();
void SimulateDemoModeSetupForTesting();
// Advances to login/update screen. Should be used in for testing only.
......
......@@ -43,6 +43,7 @@
#include "chrome/browser/chromeos/login/screens/welcome_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/test/oobe_screen_waiter.h"
#include "chrome/browser/chromeos/login/test/wizard_in_process_browser_test.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/login/ui/webui_login_view.h"
......@@ -2441,6 +2442,28 @@ IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest, ArcTosBackPressed) {
EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
}
IN_PROC_BROWSER_TEST_F(WizardControllerDemoSetupTest,
DerelictOfflineDemoSetupFlowFinished) {
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_WELCOME);
EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
WaitUntilJSIsReady();
EXPECT_CALL(*mock_welcome_screen_, Hide()).Times(1);
EXPECT_CALL(*mock_welcome_screen_, SetConfiguration(IsNull(), _)).Times(1);
EXPECT_CALL(*mock_demo_setup_screen_, Show()).Times(1);
WizardController::default_controller()->StartDerelictDemoModeSetup();
CheckCurrentScreen(OobeScreen::SCREEN_OOBE_DEMO_SETUP);
EXPECT_TRUE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
OnExit(*mock_demo_setup_screen_, ScreenExitCode::DEMO_MODE_SETUP_FINISHED);
EXPECT_TRUE(StartupUtils::IsOobeCompleted());
EXPECT_TRUE(ExistingUserController::current_controller());
EXPECT_FALSE(DemoSetupController::IsOobeDemoSetupFlowInProgress());
}
class WizardControllerOobeResumeTest : public WizardControllerTest {
protected:
WizardControllerOobeResumeTest() {}
......
......@@ -650,6 +650,9 @@ void CoreOobeHandler::GetPrimaryDisplayNameCallback(
}
void CoreOobeHandler::HandleSetupDemoMode() {
// Stop idle detection for derelict demo mode.
StopDemoModeDetection();
WizardController* wizard_controller = WizardController::default_controller();
if (wizard_controller && !wizard_controller->login_screen_started()) {
wizard_controller->StartDemoModeSetup();
......
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