Skip to content

Commit

Permalink
Introduced DeviceDisplayResolution device policy.
Browse files Browse the repository at this point in the history
DeviceDisplayResolution policy lets admin to enforce or recommend
resolution or display scale settings for displays connected to
a device.

Example of the policy value:
{
  "external_width": 1920,
  "external_height": 1080,
  "external_scale_percentage": 50,
  "internal_scale_percentage": 150,
  "recommended": true
}

It sets a 1920x1080 display mode for any external displays and
scales them to 50%, also scales the built-in display to 150%.
If "recommended" flag is set to true, user is able to override
any settings via the settings page.

Also refactored the code for DisplayRotationDefault policy to handle
all the display related policies from one place.

Bug: 499904
Change-Id: I50e135c5bc65b78ddb14dbfb40308e3e29a2b5a5
Reviewed-on: https://chromium-review.googlesource.com/c/1269533
Commit-Queue: Zakhar Voit <voit@google.com>
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
Reviewed-by: Steven Bennetts <stevenjb@chromium.org>
Reviewed-by: Sergey Poromov <poromov@chromium.org>
Reviewed-by: Alexander Hendrich <hendrich@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609673}
  • Loading branch information
zakharvoit authored and Commit Bot committed Nov 20, 2018
1 parent 837d784 commit e8f16a5
Show file tree
Hide file tree
Showing 25 changed files with 1,459 additions and 158 deletions.
4 changes: 4 additions & 0 deletions chrome/browser/chromeos/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -1503,8 +1503,12 @@ source_set("chromeos") {
"policy/device_policy_remover.h",
"policy/device_status_collector.cc",
"policy/device_status_collector.h",
"policy/display_resolution_handler.cc",
"policy/display_resolution_handler.h",
"policy/display_rotation_default_handler.cc",
"policy/display_rotation_default_handler.h",
"policy/display_settings_handler.cc",
"policy/display_settings_handler.h",
"policy/dm_token_storage.cc",
"policy/dm_token_storage.h",
"policy/enrollment_config.cc",
Expand Down
19 changes: 15 additions & 4 deletions chrome/browser/chromeos/policy/device_policy_decoder_chromeos.cc
Original file line number Diff line number Diff line change
Expand Up @@ -849,10 +849,21 @@ void DecodeGenericPolicies(const em::ChromeDeviceSettingsProto& policy,
if (policy.has_display_rotation_default()) {
const em::DisplayRotationDefaultProto& container(
policy.display_rotation_default());
policies->Set(key::kDisplayRotationDefault, POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
DecodeIntegerValue(container.display_rotation_default()),
nullptr);
if (container.has_display_rotation_default()) {
policies->Set(key::kDisplayRotationDefault, POLICY_LEVEL_MANDATORY,
POLICY_SCOPE_MACHINE, POLICY_SOURCE_CLOUD,
DecodeIntegerValue(container.display_rotation_default()),
nullptr);
}
}

if (policy.has_device_display_resolution()) {
const em::DeviceDisplayResolutionProto& container(
policy.device_display_resolution());
if (container.has_device_display_resolution()) {
SetJsonDevicePolicy(key::kDeviceDisplayResolution,
container.device_display_resolution(), policies);
}
}

if (policy.has_usb_detachable_whitelist()) {
Expand Down
244 changes: 244 additions & 0 deletions chrome/browser/chromeos/policy/display_resolution_handler.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
// Copyright 2018 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_resolution_handler.h"

#include <utility>
#include <vector>

#include "base/optional.h"
#include "base/values.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chromeos/settings/cros_settings_names.h"
#include "mojo/public/cpp/bindings/struct_traits.h"

namespace policy {

using DisplayUnitTraits =
mojo::StructTraits<::ash::mojom::DisplayUnitInfo::DataView,
::ash::mojom::DisplayUnitInfoPtr>;

struct DisplayResolutionHandler::InternalDisplaySettings {
int scale_percentage = 0;

explicit InternalDisplaySettings(int scale_percentage)
: scale_percentage(scale_percentage) {}

bool operator==(const InternalDisplaySettings& rhs) const {
return scale_percentage == rhs.scale_percentage;
}

bool operator!=(const InternalDisplaySettings& rhs) const {
return !(*this == rhs);
}

// Create display config for the internal display using policy settings from
// |internal_display_settings_|.
ash::mojom::DisplayConfigPropertiesPtr ToDisplayConfigProperties() {
auto new_config = ash::mojom::DisplayConfigProperties::New();
// Converting percentage to factor.
new_config->display_zoom_factor = scale_percentage / 100.0;
return new_config;
}

// Get settings for the internal display from
// |chromeos::kDeviceDisplayResolution| setting value.
static std::unique_ptr<InternalDisplaySettings> FromPolicySetting(
const base::DictionaryValue* pref) {
const base::Value* scale_value =
pref->FindKeyOfType(chromeos::kDeviceDisplayResolutionKeyInternalScale,
base::Value::Type::INTEGER);
return scale_value ? std::make_unique<InternalDisplaySettings>(
scale_value->GetInt())
: nullptr;
}
};

struct DisplayResolutionHandler::ExternalDisplaySettings {
bool use_native = false;
int width = 0;
int height = 0;
base::Optional<int> scale_percentage = base::nullopt;

bool operator==(const ExternalDisplaySettings& rhs) const {
return use_native == rhs.use_native && width == rhs.width &&
height == rhs.height && scale_percentage == rhs.scale_percentage;
}

bool operator!=(const ExternalDisplaySettings& rhs) const {
return !(*this == rhs);
}

// Check if either |use_native| flag is set and mode is native or the mode
// has required resolution.
bool IsSuitableDisplayMode(const ash::mojom::DisplayModePtr& mode) {
return (use_native && mode->is_native) ||
(!use_native && width == mode->size.width() &&
height == mode->size.height());
}

// Create display config for the external display using policy settings from
// |external_display_settings_|.
ash::mojom::DisplayConfigPropertiesPtr ToDisplayConfigProperties(
const std::vector<ash::mojom::DisplayModePtr>& display_modes) {
bool found_suitable_mode = false;
auto new_config = ash::mojom::DisplayConfigProperties::New();
for (const ash::mojom::DisplayModePtr& mode : display_modes) {
// Check if the current display mode has required resolution and its
// refresh rate is higher than refresh rate of the already found mode.
if (IsSuitableDisplayMode(mode) &&
(!found_suitable_mode ||
mode->refresh_rate > new_config->display_mode->refresh_rate)) {
new_config->display_mode = mode->Clone();
found_suitable_mode = true;
}
}
// If we couldn't find the required mode and and scale percentage doesn't
// need to be changed, we have nothing to do.
if (!found_suitable_mode && !scale_percentage) {
return ash::mojom::DisplayConfigPropertiesPtr();
}

if (scale_percentage) {
// Converting percentage to the factor.
new_config->display_zoom_factor = *scale_percentage / 100.0;
}

return new_config;
}

// Get settings for the external displays from
// |chromeos::kDeviceDisplayResolution| setting value;
static std::unique_ptr<ExternalDisplaySettings> FromPolicySetting(
const base::DictionaryValue* pref) {
const base::Value* width_value =
pref->FindKeyOfType(chromeos::kDeviceDisplayResolutionKeyExternalWidth,
base::Value::Type::INTEGER);
const base::Value* height_value =
pref->FindKeyOfType(chromeos::kDeviceDisplayResolutionKeyExternalHeight,
base::Value::Type::INTEGER);
const base::Value* scale_value =
pref->FindKeyOfType(chromeos::kDeviceDisplayResolutionKeyExternalScale,
base::Value::Type::INTEGER);
const base::Value* use_native_value = pref->FindKeyOfType(
chromeos::kDeviceDisplayResolutionKeyExternalUseNative,
base::Value::Type::BOOLEAN);

auto result = std::make_unique<ExternalDisplaySettings>();

// Scale can be used for both native and non-native modes
if (scale_value)
result->scale_percentage = scale_value->GetInt();

if (use_native_value && use_native_value->GetBool()) {
result->use_native = true;
return result;
}

if (width_value && height_value) {
result->width = width_value->GetInt();
result->height = height_value->GetInt();
return result;
}

return nullptr;
}
};

DisplayResolutionHandler::DisplayResolutionHandler() = default;

DisplayResolutionHandler::~DisplayResolutionHandler() = default;

const char* DisplayResolutionHandler::SettingName() {
return chromeos::kDeviceDisplayResolution;
}

// Reads |chromeos::kDeviceDisplayResolution| from CrosSettings and stores
// the settings in |recommended_|, |external_display_settings_| and
// |internal_display_settings_|. Also updates |policy_enabled_| flag.
void DisplayResolutionHandler::OnSettingUpdate() {
policy_enabled_ = false;
const base::DictionaryValue* resolution_pref = nullptr;
chromeos::CrosSettings::Get()->GetDictionary(
chromeos::kDeviceDisplayResolution, &resolution_pref);
if (!resolution_pref)
return;

std::unique_ptr<ExternalDisplaySettings> new_external_config =
ExternalDisplaySettings::FromPolicySetting(resolution_pref);
std::unique_ptr<InternalDisplaySettings> new_internal_config =
InternalDisplaySettings::FromPolicySetting(resolution_pref);

bool new_recommended = false;
policy_enabled_ = new_external_config || new_internal_config;
const base::Value* recommended_value = resolution_pref->FindKeyOfType(
chromeos::kDeviceDisplayResolutionKeyRecommended,
base::Value::Type::BOOLEAN);

if (recommended_value)
new_recommended = recommended_value->GetBool();

// We should reset locally stored settings and clear list of already updated
// displays if any of the policy values were updated.
bool should_reset_settings = false;
should_reset_settings |= !new_external_config ||
!external_display_settings_ ||
*new_external_config != *external_display_settings_;
should_reset_settings |= !new_internal_config ||
!internal_display_settings_ ||
*new_internal_config != *internal_display_settings_;
should_reset_settings |= recommended_ != new_recommended;

if (!should_reset_settings)
return;

resized_display_ids_.clear();
external_display_settings_ = std::move(new_external_config);
internal_display_settings_ = std::move(new_internal_config);
recommended_ = new_recommended;
}

// Applies settings received with |OnSettingUpdate| to each supported display
// from |info_list| if |policy_enabled_| is true.
void DisplayResolutionHandler::ApplyChanges(
ash::mojom::CrosDisplayConfigController* cros_display_config,
const std::vector<ash::mojom::DisplayUnitInfoPtr>& info_list) {
if (!policy_enabled_)
return;
for (const ash::mojom::DisplayUnitInfoPtr& display_unit_info : info_list) {
std::string display_id = display_unit_info->id;
// If policy value is marked as "recommended" we need to change the
// resolution just once for each display. So we're just skipping the display
// if it was resized since last settings update.
if (recommended_ &&
resized_display_ids_.find(display_id) != resized_display_ids_.end()) {
continue;
}

ash::mojom::DisplayConfigPropertiesPtr new_config;
if (display_unit_info->is_internal && internal_display_settings_) {
new_config = internal_display_settings_->ToDisplayConfigProperties();
} else if (!display_unit_info->is_internal && external_display_settings_) {
new_config = external_display_settings_->ToDisplayConfigProperties(
DisplayUnitTraits::available_display_modes(display_unit_info));
}

if (!new_config)
continue;

resized_display_ids_.insert(display_id);
cros_display_config->SetDisplayProperties(
display_unit_info->id, std::move(new_config),
base::BindOnce([](ash::mojom::DisplayConfigResult result) {
if (result == ash::mojom::DisplayConfigResult::kSuccess) {
VLOG(1) << "Successfully changed display mode.";
} else {
LOG(ERROR) << "Couldn't change display mode. Error code: "
<< result;
}
}));
}
}

} // namespace policy
61 changes: 61 additions & 0 deletions chrome/browser/chromeos/policy/display_resolution_handler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright 2018 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.

#ifndef CHROME_BROWSER_CHROMEOS_POLICY_DISPLAY_RESOLUTION_HANDLER_H_
#define CHROME_BROWSER_CHROMEOS_POLICY_DISPLAY_RESOLUTION_HANDLER_H_

#include <memory>
#include <set>
#include <string>
#include <vector>

#include "ash/public/interfaces/cros_display_config.mojom.h"
#include "chrome/browser/chromeos/policy/display_settings_handler.h"

namespace policy {

// Implements DeviceDisplayResolution device policy.
//
// Whenever there is a change in the display configration, any new display will
// be resized according to the policy (only if the policy is enabled and the
// display supports specified resolution and scale factor).
//
// Whenever there is a change in |kDeviceDisplayResolution| setting from
// CrosSettings, the new policy is reapplied to all displays.
//
// If the specified resolution or scale factor is not supported by some display,
// the resolution won't change.
//
// Once resolution or scale factor for some display was set by this policy it
// won't be reapplied until next reboot or policy change (i.e. user can manually
// override the settings for that display via settings page).
class DisplayResolutionHandler : public DisplaySettingsPolicyHandler {
public:
DisplayResolutionHandler();

~DisplayResolutionHandler() override;

// DisplaySettingsPolicyHandler
const char* SettingName() override;
void OnSettingUpdate() override;
void ApplyChanges(
ash::mojom::CrosDisplayConfigController* cros_display_config,
const std::vector<ash::mojom::DisplayUnitInfoPtr>& info_list) override;

private:
struct InternalDisplaySettings;
struct ExternalDisplaySettings;

bool policy_enabled_ = false;
bool recommended_ = false;
std::unique_ptr<ExternalDisplaySettings> external_display_settings_;
std::unique_ptr<InternalDisplaySettings> internal_display_settings_;
std::set<std::string> resized_display_ids_;

DISALLOW_COPY_AND_ASSIGN(DisplayResolutionHandler);
};

} // namespace policy

#endif // CHROME_BROWSER_CHROMEOS_POLICY_DISPLAY_RESOLUTION_HANDLER_H_
Loading

0 comments on commit e8f16a5

Please sign in to comment.