blob: 485548a0766a6fdd72610958c42136bb6a04515d [file] [log] [blame]
// Copyright 2015 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/policy/display_rotation_default_handler.h"
#include <stdint.h>
#include <memory>
#include "ash/shell.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/location.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/policy/device_policy_builder.h"
#include "chrome/browser/chromeos/policy/device_policy_cros_browser_test.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_cryptohome_client.h"
#include "chromeos/dbus/fake_session_manager_client.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/settings/cros_settings_names.h"
#include "components/policy/proto/chrome_device_policy.pb.h"
#include "content/public/test/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/display/display.h"
#include "ui/display/display_layout.h"
#include "ui/display/manager/display_manager.h"
namespace em = enterprise_management;
namespace {
display::DisplayManager* GetDisplayManager() {
return ash::Shell::Get()->display_manager();
}
display::Display::Rotation GetRotationOfFirstDisplay() {
const display::DisplayManager* const display_manager = GetDisplayManager();
const int64_t first_display_id = display_manager->first_display_id();
const display::Display& first_display =
display_manager->GetDisplayForId(first_display_id);
return first_display.rotation();
}
// Fails the test and returns ROTATE_0 if there is no second display.
display::Display::Rotation GetRotationOfSecondDisplay() {
const display::DisplayManager* const display_manager = GetDisplayManager();
if (display_manager->GetNumDisplays() < 2) {
ADD_FAILURE()
<< "Requested rotation of second display while there was only one.";
return display::Display::ROTATE_0;
}
const display::DisplayIdList display_id_pair =
display_manager->GetCurrentDisplayIdList();
const display::Display& second_display =
display_manager->GetDisplayForId(display_id_pair[1]);
return second_display.rotation();
}
} // anonymous namespace
namespace policy {
class DisplayRotationDefaultTest
: public policy::DevicePolicyCrosBrowserTest,
public testing::WithParamInterface<display::Display::Rotation> {
public:
DisplayRotationDefaultTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(chromeos::switches::kLoginManager);
command_line->AppendSwitch(chromeos::switches::kForceLoginManagerInTests);
}
void SetUpInProcessBrowserTestFixture() override {
InstallOwnerKey();
MarkAsEnterpriseOwned();
DevicePolicyCrosBrowserTest::SetUpInProcessBrowserTestFixture();
}
void TearDownOnMainThread() override {
// If the login display is still showing, exit gracefully.
if (chromeos::LoginDisplayHost::default_host()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(&chrome::AttemptExit));
content::RunMessageLoop();
}
}
protected:
void SetPolicy(int rotation) {
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
proto.mutable_display_rotation_default()->set_display_rotation_default(
static_cast<em::DisplayRotationDefaultProto::Rotation>(rotation));
RefreshPolicyAndWaitUntilDeviceSettingsUpdated();
}
// This is needed to test that refreshing the settings without change to the
// DisplayRotationDefault policy will not rotate the display again.
void SetADifferentPolicy() {
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
const bool clock24 = proto.use_24hour_clock().use_24hour_clock();
proto.mutable_use_24hour_clock()->set_use_24hour_clock(!clock24);
RefreshPolicyAndWaitUntilDeviceSettingsUpdated();
}
void UnsetPolicy() {
em::ChromeDeviceSettingsProto& proto(device_policy()->payload());
proto.clear_display_rotation_default();
RefreshPolicyAndWaitUntilDeviceSettingsUpdated();
}
// Creates second display if there is none yet, or removes it if there is one.
void ToggleSecondDisplay() {
GetDisplayManager()->AddRemoveDisplay();
base::RunLoop run_loop;
run_loop.RunUntilIdle();
}
void RefreshPolicyAndWaitUntilDeviceSettingsUpdated() {
base::RunLoop run_loop;
// For calls from SetPolicy().
std::unique_ptr<chromeos::CrosSettings::ObserverSubscription> observer =
chromeos::CrosSettings::Get()->AddSettingsObserver(
chromeos::kDisplayRotationDefault, run_loop.QuitClosure());
// For calls from SetADifferentPolicy().
std::unique_ptr<chromeos::CrosSettings::ObserverSubscription> observer2 =
chromeos::CrosSettings::Get()->AddSettingsObserver(
chromeos::kSystemUse24HourClock, run_loop.QuitClosure());
RefreshDevicePolicy();
run_loop.Run();
}
private:
DISALLOW_COPY_AND_ASSIGN(DisplayRotationDefaultTest);
};
// If display::Display::Rotation is ever modified and this test fails, there are
// hardcoded enum-values in the following files that might need adjustment:
// * this file: range check in this function, initializations, expected values,
// the list of parameters at the bottom of the file
// * display_rotation_default_handler.cc: Range check in UpdateFromCrosSettings,
// initialization to ROTATE_0
// * display_rotation_default_handler.h: initialization to ROTATE_0
// * components/policy/proto/chrome_device_policy.proto:
// DisplayRotationDefaultProto::Rotation should match one to one
IN_PROC_BROWSER_TEST_P(DisplayRotationDefaultTest, EnumsInSync) {
const display::Display::Rotation rotation = GetParam();
EXPECT_EQ(em::DisplayRotationDefaultProto::Rotation_ARRAYSIZE,
display::Display::ROTATE_270 + 1)
<< "Enums display::Display::Rotation and "
<< "em::DisplayRotationDefaultProto::Rotation are not in sync.";
EXPECT_TRUE(em::DisplayRotationDefaultProto::Rotation_IsValid(rotation))
<< rotation << " is invalid as rotation. All valid values lie in "
<< "the range from " << em::DisplayRotationDefaultProto::Rotation_MIN
<< " to " << em::DisplayRotationDefaultProto::Rotation_MAX << ".";
}
IN_PROC_BROWSER_TEST_P(DisplayRotationDefaultTest, FirstDisplay) {
const display::Display::Rotation policy_rotation = GetParam();
EXPECT_EQ(display::Display::ROTATE_0, GetRotationOfFirstDisplay())
<< "Initial primary rotation before policy";
SetPolicy(policy_rotation);
int settings_rotation;
EXPECT_TRUE(chromeos::CrosSettings::Get()->GetInteger(
chromeos::kDisplayRotationDefault, &settings_rotation));
EXPECT_EQ(policy_rotation, settings_rotation)
<< "Value of CrosSettings after policy value changed";
EXPECT_EQ(policy_rotation, GetRotationOfFirstDisplay())
<< "Rotation of primary display after policy";
}
IN_PROC_BROWSER_TEST_P(DisplayRotationDefaultTest, RefreshSecondDisplay) {
const display::Display::Rotation policy_rotation = GetParam();
ToggleSecondDisplay();
EXPECT_EQ(display::Display::ROTATE_0, GetRotationOfSecondDisplay())
<< "Rotation of secondary display before policy";
SetPolicy(policy_rotation);
EXPECT_EQ(policy_rotation, GetRotationOfSecondDisplay())
<< "Rotation of already connected secondary display after policy";
}
IN_PROC_BROWSER_TEST_P(DisplayRotationDefaultTest, ConnectSecondDisplay) {
const display::Display::Rotation policy_rotation = GetParam();
SetPolicy(policy_rotation);
ToggleSecondDisplay();
EXPECT_EQ(policy_rotation, GetRotationOfFirstDisplay())
<< "Rotation of primary display after policy";
EXPECT_EQ(policy_rotation, GetRotationOfSecondDisplay())
<< "Rotation of newly connected secondary display after policy";
}
IN_PROC_BROWSER_TEST_P(DisplayRotationDefaultTest, UserInteraction) {
const display::Display::Rotation policy_rotation = GetParam();
const display::Display::Rotation user_rotation = display::Display::ROTATE_90;
GetDisplayManager()->SetDisplayRotation(
GetDisplayManager()->first_display_id(), user_rotation,
display::Display::ROTATION_SOURCE_USER);
EXPECT_EQ(user_rotation, GetRotationOfFirstDisplay())
<< "Rotation of primary display after user change";
SetPolicy(policy_rotation);
EXPECT_EQ(policy_rotation, GetRotationOfFirstDisplay())
<< "Rotation of primary display after policy overrode user change";
GetDisplayManager()->SetDisplayRotation(
GetDisplayManager()->first_display_id(), user_rotation,
display::Display::ROTATION_SOURCE_USER);
EXPECT_EQ(user_rotation, GetRotationOfFirstDisplay())
<< "Rotation of primary display after user overrode policy change";
SetADifferentPolicy();
EXPECT_EQ(user_rotation, GetRotationOfFirstDisplay())
<< "Rotation of primary display after policy reloaded without change";
}
IN_PROC_BROWSER_TEST_P(DisplayRotationDefaultTest, SetAndUnsetPolicy) {
const display::Display::Rotation policy_rotation = GetParam();
SetPolicy(policy_rotation);
UnsetPolicy();
EXPECT_EQ(policy_rotation, GetRotationOfFirstDisplay())
<< "Rotation of primary display after policy was set and removed.";
}
IN_PROC_BROWSER_TEST_P(DisplayRotationDefaultTest,
SetAndUnsetPolicyWithUserInteraction) {
const display::Display::Rotation policy_rotation = GetParam();
const display::Display::Rotation user_rotation = display::Display::ROTATE_90;
SetPolicy(policy_rotation);
GetDisplayManager()->SetDisplayRotation(
GetDisplayManager()->first_display_id(), user_rotation,
display::Display::ROTATION_SOURCE_USER);
UnsetPolicy();
EXPECT_EQ(user_rotation, GetRotationOfFirstDisplay())
<< "Rotation of primary display after policy was set to "
<< policy_rotation << ", user changed the rotation to " << user_rotation
<< ", and policy was removed.";
}
INSTANTIATE_TEST_CASE_P(PolicyDisplayRotationDefault,
DisplayRotationDefaultTest,
testing::Values(display::Display::ROTATE_0,
display::Display::ROTATE_90,
display::Display::ROTATE_180,
display::Display::ROTATE_270));
// This class tests that the policy is reapplied after a reboot. To persist from
// PRE_Reboot to Reboot, the policy is inserted into a FakeSessionManagerClient.
// From there, it travels to DeviceSettingsProvider, whose UpdateFromService()
// method caches the policy (using device_settings_cache::Store()).
// In the main test, the FakeSessionManagerClient is not fully initialized.
// Thus, DeviceSettingsProvider falls back on the cached values (using
// device_settings_cache::Retrieve()).
class DisplayRotationBootTest
: public InProcessBrowserTest,
public testing::WithParamInterface<display::Display::Rotation> {
protected:
DisplayRotationBootTest()
: fake_session_manager_client_(new chromeos::FakeSessionManagerClient) {}
~DisplayRotationBootTest() override {}
void SetUpInProcessBrowserTestFixture() override {
chromeos::DBusThreadManager::GetSetterForTesting()->SetSessionManagerClient(
std::unique_ptr<chromeos::SessionManagerClient>(
fake_session_manager_client_));
chromeos::DBusThreadManager::GetSetterForTesting()->SetCryptohomeClient(
std::unique_ptr<chromeos::CryptohomeClient>(
new chromeos::FakeCryptohomeClient));
test_helper_.InstallOwnerKey();
test_helper_.MarkAsEnterpriseOwned();
}
chromeos::FakeSessionManagerClient* fake_session_manager_client_;
policy::DevicePolicyCrosTestHelper test_helper_;
};
IN_PROC_BROWSER_TEST_P(DisplayRotationBootTest, PRE_Reboot) {
const display::Display::Rotation policy_rotation = GetParam();
const display::Display::Rotation user_rotation = display::Display::ROTATE_180;
// Set policy.
policy::DevicePolicyBuilder* const device_policy(
test_helper_.device_policy());
em::ChromeDeviceSettingsProto& proto(device_policy->payload());
proto.mutable_display_rotation_default()->set_display_rotation_default(
static_cast<em::DisplayRotationDefaultProto::Rotation>(policy_rotation));
base::RunLoop run_loop;
std::unique_ptr<chromeos::CrosSettings::ObserverSubscription> observer =
chromeos::CrosSettings::Get()->AddSettingsObserver(
chromeos::kDisplayRotationDefault, run_loop.QuitClosure());
device_policy->SetDefaultSigningKey();
device_policy->Build();
fake_session_manager_client_->set_device_policy(device_policy->GetBlob());
fake_session_manager_client_->OnPropertyChangeComplete(true);
run_loop.Run();
// Check the display's rotation.
display::DisplayManager* const display_manager = GetDisplayManager();
const int64_t first_display_id = display_manager->first_display_id();
const display::Display& first_display =
display_manager->GetDisplayForId(first_display_id);
EXPECT_EQ(policy_rotation, first_display.rotation());
// Let the user rotate the display to a different orientation, to check that
// the policy value is restored after reboot.
display_manager->SetDisplayRotation(first_display_id, user_rotation,
display::Display::ROTATION_SOURCE_USER);
EXPECT_EQ(user_rotation, first_display.rotation());
}
IN_PROC_BROWSER_TEST_P(DisplayRotationBootTest, Reboot) {
const display::Display::Rotation policy_rotation = GetParam();
// Check that the policy rotation is restored.
EXPECT_EQ(policy_rotation, GetRotationOfFirstDisplay());
}
INSTANTIATE_TEST_CASE_P(PolicyDisplayRotationDefault,
DisplayRotationBootTest,
testing::Values(display::Display::ROTATE_0,
display::Display::ROTATE_90,
display::Display::ROTATE_180,
display::Display::ROTATE_270));
} // namespace policy