| // Copyright 2014 The Chromium Authors | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include <memory> | 
 | #include <string> | 
 |  | 
 | #include "ash/constants/ash_switches.h" | 
 | #include "ash/public/cpp/login_screen_test_api.h" | 
 | #include "base/command_line.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/logging.h" | 
 | #include "base/memory/scoped_refptr.h" | 
 | #include "base/run_loop.h" | 
 | #include "chrome/browser/ash/login/login_wizard.h" | 
 | #include "chrome/browser/ash/login/test/device_state_mixin.h" | 
 | #include "chrome/browser/ash/login/test/login_manager_mixin.h" | 
 | #include "chrome/browser/ash/login/test/network_portal_detector_mixin.h" | 
 | #include "chrome/browser/ash/login/test/oobe_base_test.h" | 
 | #include "chrome/browser/ash/login/test/oobe_screen_waiter.h" | 
 | #include "chrome/browser/ash/login/test/scoped_policy_update.h" | 
 | #include "chrome/browser/ash/login/wizard_controller.h" | 
 | #include "chrome/browser/ash/system/device_disabling_manager.h" | 
 | #include "chrome/browser/browser_process.h" | 
 | #include "chrome/browser/browser_process_platform_part.h" | 
 | #include "chrome/browser/ui/ash/login/login_display_host.h" | 
 | #include "chrome/browser/ui/webui/ash/login/device_disabled_screen_handler.h" | 
 | #include "chrome/browser/ui/webui/ash/login/network_state_informer.h" | 
 | #include "chrome/browser/ui/webui/ash/login/oobe_ui.h" | 
 | #include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h" | 
 | #include "chromeos/ash/components/dbus/shill/shill_manager_client.h" | 
 | #include "chromeos/ash/components/dbus/shill/shill_service_client.h" | 
 | #include "chromeos/ash/components/settings/cros_settings.h" | 
 | #include "chromeos/ash/components/settings/cros_settings_names.h" | 
 | #include "components/policy/proto/device_management_backend.pb.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "content/public/test/browser_test.h" | 
 | #include "content/public/test/browser_test_utils.h" | 
 | #include "dbus/object_path.h" | 
 |  | 
 | namespace ash { | 
 | namespace system { | 
 |  | 
 | namespace { | 
 |  | 
 | void ErrorCallbackFunction(const std::string& error_name, | 
 |                            const std::string& error_message) { | 
 |   LOG(ERROR) << "Shill Error: " << error_name << " : " << error_message; | 
 | } | 
 |  | 
 | bool DeviceDisabledScreenShown() { | 
 |   WizardController* const wizard_controller = | 
 |       WizardController::default_controller(); | 
 |   EXPECT_TRUE(wizard_controller); | 
 |   return wizard_controller && | 
 |          wizard_controller->current_screen() == | 
 |              wizard_controller->GetScreen(DeviceDisabledScreenView::kScreenId); | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | class DeviceDisablingTest | 
 |     : public OobeBaseTest, | 
 |       public NetworkStateInformer::NetworkStateInformerObserver { | 
 |  public: | 
 |   DeviceDisablingTest() = default; | 
 |  | 
 |   DeviceDisablingTest(const DeviceDisablingTest&) = delete; | 
 |   DeviceDisablingTest& operator=(const DeviceDisablingTest&) = delete; | 
 |  | 
 |   // Sets up a device state blob that indicates the device is disabled. | 
 |   void SetDeviceDisabledPolicy(); | 
 |  | 
 |   // Sets up a device state blob that indicates the device is disabled, triggers | 
 |   // a policy plus device state fetch and waits for it to succeed. | 
 |   void MarkDisabledAndWaitForPolicyFetch(); | 
 |  | 
 |   std::string GetCurrentScreenName(content::WebContents* web_contents); | 
 |  | 
 |  protected: | 
 |   // OobeBaseTest: | 
 |   void SetUpOnMainThread() override; | 
 |  | 
 |   // NetworkStateInformer::NetworkStateInformerObserver: | 
 |   void UpdateState(NetworkError::ErrorReason reason) override; | 
 |  | 
 |   std::unique_ptr<base::RunLoop> network_state_change_wait_run_loop_; | 
 |   NetworkPortalDetectorMixin network_portal_detector_{&mixin_host_}; | 
 |  | 
 |  private: | 
 |   DeviceStateMixin device_state_{ | 
 |       &mixin_host_, DeviceStateMixin::State::OOBE_COMPLETED_CLOUD_ENROLLED}; | 
 | }; | 
 |  | 
 | void DeviceDisablingTest::SetDeviceDisabledPolicy() { | 
 |   // Prepare a policy fetch response that indicates the device is disabled. | 
 |   std::unique_ptr<ScopedDevicePolicyUpdate> policy_update = | 
 |       device_state_.RequestDevicePolicyUpdate(); | 
 |   policy_update->policy_data()->mutable_device_state()->set_device_mode( | 
 |       enterprise_management::DeviceState::DEVICE_MODE_DISABLED); | 
 | } | 
 |  | 
 | void DeviceDisablingTest::MarkDisabledAndWaitForPolicyFetch() { | 
 |   base::RunLoop run_loop; | 
 |   // Set up an observer that will wait for the disabled setting to change. | 
 |   base::CallbackListSubscription subscription = | 
 |       CrosSettings::Get()->AddSettingsObserver(kDeviceDisabled, | 
 |                                                run_loop.QuitClosure()); | 
 |   SetDeviceDisabledPolicy(); | 
 |   // Trigger a policy fetch. | 
 |   FakeSessionManagerClient::Get()->OnPropertyChangeComplete(true); | 
 |   // Wait for the policy fetch to complete and the disabled setting to change. | 
 |   run_loop.Run(); | 
 | } | 
 |  | 
 | void DeviceDisablingTest::SetUpOnMainThread() { | 
 |   network_state_change_wait_run_loop_ = std::make_unique<base::RunLoop>(); | 
 |  | 
 |   OobeBaseTest::SetUpOnMainThread(); | 
 |  | 
 |   // Set up fake networks. | 
 |   ShillManagerClient::Get()->GetTestInterface()->SetupDefaultEnvironment(); | 
 | } | 
 |  | 
 | void DeviceDisablingTest::UpdateState(NetworkError::ErrorReason reason) { | 
 |   network_state_change_wait_run_loop_->Quit(); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(DeviceDisablingTest, DisableDuringNormalOperation) { | 
 |   MarkDisabledAndWaitForPolicyFetch(); | 
 |   // Check for WizardController state. | 
 |   OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait(); | 
 |  | 
 |   EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible()); | 
 | } | 
 |  | 
 | // Verifies that device disabling works when the ephemeral users policy is | 
 | // enabled. This case warrants its own test because the UI behaves somewhat | 
 | // differently when the policy is set: A background job runs on startup that | 
 | // causes the UI to try and show the login screen after some delay. It must | 
 | // be ensured that the login screen does not show and does not clobber the | 
 | // disabled screen. | 
 | IN_PROC_BROWSER_TEST_F(DeviceDisablingTest, DisableWithEphemeralUsers) { | 
 |   // Connect to the fake Ethernet network. This ensures that Chrome OS will not | 
 |   // try to show the offline error screen. | 
 |   base::RunLoop connect_run_loop; | 
 |   ShillServiceClient::Get()->Connect(dbus::ObjectPath("/service/eth1"), | 
 |                                      connect_run_loop.QuitClosure(), | 
 |                                      base::BindOnce(&ErrorCallbackFunction)); | 
 |   connect_run_loop.Run(); | 
 |  | 
 |   // Skip to the login screen. | 
 |   OobeScreenWaiter(GetFirstSigninScreen()).Wait(); | 
 |  | 
 |   // Mark the device as disabled and wait until cros settings update. | 
 |   MarkDisabledAndWaitForPolicyFetch(); | 
 |  | 
 |   // Check for WizardController state. | 
 |   OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait(); | 
 |  | 
 |   // Disconnect from the fake Ethernet network. | 
 |   const LoginDisplayHost* host = LoginDisplayHost::default_host(); | 
 |   OobeUI* const oobe_ui = host->GetOobeUI(); | 
 |   ASSERT_TRUE(oobe_ui); | 
 |   const scoped_refptr<NetworkStateInformer> network_state_informer = | 
 |       oobe_ui->network_state_informer_for_test(); | 
 |   ASSERT_TRUE(network_state_informer); | 
 |   network_state_informer->AddObserver(this); | 
 |   network_portal_detector_.SimulateDefaultNetworkState( | 
 |       NetworkPortalDetectorMixin::NetworkStatus::kOffline); | 
 |   network_state_change_wait_run_loop_->Run(); | 
 |   network_state_informer->RemoveObserver(this); | 
 |   base::RunLoop().RunUntilIdle(); | 
 |  | 
 |   // Verify that the offline error screen was not shown and the device disabled | 
 |   // screen is still being shown instead. | 
 |   // Check for WizardController state. | 
 |   OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait(); | 
 | } | 
 |  | 
 | class DeviceDisablingWithUsersTest : public DeviceDisablingTest { | 
 |  public: | 
 |   DeviceDisablingWithUsersTest() { login_manager_.AppendRegularUsers(2); } | 
 |  | 
 |  private: | 
 |   LoginManagerMixin login_manager_{&mixin_host_}; | 
 | }; | 
 |  | 
 | // Checks that OOBE dialog is not hidden when the device disabled screen is | 
 | // shown and "StartSignInScreen" is called. | 
 | IN_PROC_BROWSER_TEST_F(DeviceDisablingWithUsersTest, DialogNotHidden) { | 
 |   EXPECT_TRUE(ash::LoginScreenTestApi::ClickAddUserButton()); | 
 |   EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible()); | 
 |   OobeScreenWaiter(GetFirstSigninScreen()).Wait(); | 
 |   MarkDisabledAndWaitForPolicyFetch(); | 
 |   OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait(); | 
 |   LoginDisplayHost::default_host()->StartSignInScreen(); | 
 |  | 
 |   // Dialog should not be hidden. | 
 |   EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible()); | 
 | } | 
 |  | 
 | // Sets the device disabled policy before the browser is started. | 
 | class PresetPolicyDeviceDisablingTest : public DeviceDisablingTest { | 
 |  public: | 
 |   PresetPolicyDeviceDisablingTest() = default; | 
 |  | 
 |   PresetPolicyDeviceDisablingTest(const PresetPolicyDeviceDisablingTest&) = | 
 |       delete; | 
 |   PresetPolicyDeviceDisablingTest& operator=( | 
 |       const PresetPolicyDeviceDisablingTest&) = delete; | 
 |  | 
 |  protected: | 
 |   // DeviceDisablingTest: | 
 |   void SetUpInProcessBrowserTestFixture() override { | 
 |     DeviceDisablingTest::SetUpInProcessBrowserTestFixture(); | 
 |     SetDeviceDisabledPolicy(); | 
 |   } | 
 | }; | 
 |  | 
 | // Same test as the one in DeviceDisablingTest, except the policy is being set | 
 | // before Chrome process is started. This test covers a crash (crbug.com/709518) | 
 | // in DeviceDisabledScreen where it would try to access DeviceDisablingManager | 
 | // even though it wasn't yet constructed fully. | 
 | IN_PROC_BROWSER_TEST_F(PresetPolicyDeviceDisablingTest, | 
 |                        DisableBeforeStartup) { | 
 |   EXPECT_TRUE(DeviceDisabledScreenShown()); | 
 |   EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible()); | 
 | } | 
 |  | 
 | class DeviceDisablingBeforeLoginHostCreated | 
 |     : public PresetPolicyDeviceDisablingTest { | 
 |  public: | 
 |   DeviceDisablingBeforeLoginHostCreated() { | 
 |     // Start with user pods. | 
 |     login_mixin_.AppendManagedUsers(2); | 
 |   } | 
 |  | 
 |   bool SetUpUserDataDirectory() override { | 
 |     // LoginManagerMixin sets up command line in the SetUpUserDataDirectory. | 
 |     if (!PresetPolicyDeviceDisablingTest::SetUpUserDataDirectory()) | 
 |       return false; | 
 |     // Postpone login host creation. | 
 |     base::CommandLine::ForCurrentProcess()->RemoveSwitch( | 
 |         switches::kForceLoginManagerInTests); | 
 |     return true; | 
 |   } | 
 |  | 
 |   bool ShouldWaitForOobeUI() override { return false; } | 
 |  | 
 |  protected: | 
 |   LoginManagerMixin login_mixin_{&mixin_host_}; | 
 | }; | 
 |  | 
 | // Sometimes LoginHost creation postponed (e.g. due to language switch | 
 | // https://crbug.com/1065569). This tests checks this flow. | 
 | IN_PROC_BROWSER_TEST_F(DeviceDisablingBeforeLoginHostCreated, | 
 |                        ShowsDisabledScreen) { | 
 |   EXPECT_TRUE( | 
 |       system::DeviceDisablingManager::IsDeviceDisabledDuringNormalOperation()); | 
 |   EXPECT_EQ(nullptr, LoginDisplayHost::default_host()); | 
 |   EXPECT_NE(nullptr, | 
 |             g_browser_process->platform_part()->device_disabling_manager()); | 
 |   ShowLoginWizard(ash::OOBE_SCREEN_UNKNOWN); | 
 |   // Check for WizardController state. | 
 |   OobeScreenWaiter(DeviceDisabledScreenView::kScreenId).Wait(); | 
 |  | 
 |   EXPECT_TRUE(ash::LoginScreenTestApi::IsOobeDialogVisible()); | 
 | } | 
 |  | 
 | }  // namespace system | 
 | }  // namespace ash |