chromium/ui/display/manager/test/test_native_display_delegate.cc

// 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 "ui/display/manager/test/test_native_display_delegate.h"

#include "base/functional/bind.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/strings/strcat.h"
#include "base/task/single_thread_task_runner.h"
#include "ui/display/manager/test/action_logger.h"
#include "ui/display/types/display_mode.h"
#include "ui/display/types/display_snapshot.h"
#include "ui/display/types/native_display_observer.h"
#include "ui/gfx/geometry/size.h"

namespace display::test {

std::string GetModesetFlag(display::ModesetFlags modeset_flags) {
  std::string flags_str;
  if (modeset_flags.Has(display::ModesetFlag::kTestModeset)) {
    flags_str = base::StrCat({flags_str, kTestModesetStr, ","});
  }
  if (modeset_flags.Has(display::ModesetFlag::kCommitModeset)) {
    flags_str = base::StrCat({flags_str, kCommitModesetStr, ","});
  }
  if (modeset_flags.Has(display::ModesetFlag::kSeamlessModeset)) {
    flags_str = base::StrCat({flags_str, kSeamlessModesetStr, ","});
  }

  // Remove trailing comma.
  if (!flags_str.empty())
    flags_str.resize(flags_str.size() - 1);
  return flags_str;
}

TestNativeDisplayDelegate::TestNativeDisplayDelegate(ActionLogger* log)
    : max_configurable_pixels_(0),
      get_hdcp_expectation_(true),
      set_hdcp_expectation_(true),
      hdcp_state_(HDCP_STATE_UNDESIRED),
      content_protection_method_(CONTENT_PROTECTION_METHOD_NONE),
      run_async_(false),
      log_(log) {}

TestNativeDisplayDelegate::~TestNativeDisplayDelegate() = default;

const std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>>
TestNativeDisplayDelegate::GetOutputs() const {
  std::vector<raw_ptr<DisplaySnapshot, VectorExperimental>> outputs;
  for (const auto& output : outputs_) {
    outputs.push_back(output.get());
  }
  return outputs;
}

void TestNativeDisplayDelegate::SetOutputs(
    std::vector<std::unique_ptr<DisplaySnapshot>> outputs) {
  std::move(begin(outputs_), end(outputs_),
            std::back_inserter(cached_outputs_));
  outputs_ = std::move(outputs);
}

void TestNativeDisplayDelegate::Initialize() {
  log_->AppendAction(kInit);
}

void TestNativeDisplayDelegate::TakeDisplayControl(
    DisplayControlCallback callback) {
  log_->AppendAction(kTakeDisplayControl);
  std::move(callback).Run(true);
}

void TestNativeDisplayDelegate::RelinquishDisplayControl(
    DisplayControlCallback callback) {
  log_->AppendAction(kRelinquishDisplayControl);
  std::move(callback).Run(true);
}

void TestNativeDisplayDelegate::GetDisplays(GetDisplaysCallback callback) {
  // This mimics the behavior of Ozone DRM when new display state arrives.
  for (NativeDisplayObserver& observer : observers_)
    observer.OnDisplaySnapshotsInvalidated();
  cached_outputs_.clear();

  if (run_async_) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), GetOutputs()));
  } else {
    std::move(callback).Run(GetOutputs());
  }
}

bool TestNativeDisplayDelegate::Configure(
    const display::DisplayConfigurationParams& display_config_params) {
  log_->AppendAction(GetCrtcAction(display_config_params));

  if (max_configurable_pixels_ == 0)
    return true;
  else if (max_configurable_pixels_ < 0)
    return false;

  if (display_config_params.mode) {
    return display_config_params.mode->size().GetArea() <=
           max_configurable_pixels_;
  }

  return true;
}

bool TestNativeDisplayDelegate::IsConfigurationWithinSystemBandwidth(
    const std::vector<display::DisplayConfigurationParams>& config_requests) {
  if (system_bandwidth_limit_ == 0)
    return true;

  // We need a copy of the current state to account for current configuration.
  // But we can't overwrite it yet because we may fail to configure
  base::flat_map<int64_t, int> requested_ids_with_bandwidth =
      display_id_to_used_system_bw_;
  for (const DisplayConfigurationParams& config : config_requests) {
    requested_ids_with_bandwidth[config.id] =
        config.mode ? config.mode->size().GetArea() : 0;
  }

  int requested_bandwidth = 0;
  for (const auto& it : requested_ids_with_bandwidth) {
    requested_bandwidth += it.second;
  }

  return requested_bandwidth <= system_bandwidth_limit_;
}

void TestNativeDisplayDelegate::SaveCurrentConfigSystemBandwidth(
    const std::vector<display::DisplayConfigurationParams>& config_requests) {
  // On a successful configuration, we update the current state to reflect the
  // current system usage.
  for (const DisplayConfigurationParams& config : config_requests) {
    display_id_to_used_system_bw_[config.id] =
        config.mode ? config.mode->size().GetArea() : 0;
  }
}

void TestNativeDisplayDelegate::Configure(
    const std::vector<display::DisplayConfigurationParams>& config_requests,
    ConfigureCallback callback,
    display::ModesetFlags modeset_flags) {
  log_->AppendAction(GetModesetFlag(modeset_flags));
  bool config_success = true;
  for (const auto& config : config_requests)
    config_success &= Configure(config);

  config_success &= IsConfigurationWithinSystemBandwidth(config_requests);

  if (config_success)
    SaveCurrentConfigSystemBandwidth(config_requests);

  std::string config_outcome = "outcome: ";
  config_outcome += config_success ? "success" : "failure";
  log_->AppendAction(config_outcome);

  if (run_async_) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(std::move(callback), config_requests, config_success));
  } else {
    std::move(callback).Run(config_requests, config_success);
  }
}

void TestNativeDisplayDelegate::SetHdcpKeyProp(
    int64_t display_id,
    const std::string& key,
    SetHdcpKeyPropCallback callback) {
  log_->AppendAction(GetSetHdcpKeyPropAction(display_id, true));

  if (run_async_) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), true));
  } else {
    std::move(callback).Run(true);
  }
}

void TestNativeDisplayDelegate::GetHDCPState(const DisplaySnapshot& output,
                                             GetHDCPStateCallback callback) {
  if (run_async_) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), get_hdcp_expectation_,
                                  hdcp_state_, content_protection_method_));
  } else {
    std::move(callback).Run(get_hdcp_expectation_, hdcp_state_,
                            content_protection_method_);
  }
}

void TestNativeDisplayDelegate::SetHDCPState(
    const DisplaySnapshot& output,
    HDCPState state,
    ContentProtectionMethod protection_method,
    SetHDCPStateCallback callback) {
  if (run_async_) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE,
        base::BindOnce(&TestNativeDisplayDelegate::DoSetHDCPState,
                       base::Unretained(this), output.display_id(), state,
                       protection_method, std::move(callback)));
  } else {
    DoSetHDCPState(output.display_id(), state, protection_method,
                   std::move(callback));
  }
}

void TestNativeDisplayDelegate::DoSetHDCPState(
    int64_t display_id,
    HDCPState state,
    ContentProtectionMethod protection_method,
    SetHDCPStateCallback callback) {
  log_->AppendAction(
      GetSetHDCPStateAction(display_id, state, protection_method));

  switch (state) {
    case HDCP_STATE_ENABLED:
      NOTREACHED_IN_MIGRATION();
      break;

    case HDCP_STATE_DESIRED:
      hdcp_state_ =
          set_hdcp_expectation_ ? HDCP_STATE_ENABLED : HDCP_STATE_DESIRED;
      break;

    case HDCP_STATE_UNDESIRED:
      if (set_hdcp_expectation_)
        hdcp_state_ = HDCP_STATE_UNDESIRED;
      break;
  }

  content_protection_method_ = set_hdcp_expectation_
                                   ? protection_method
                                   : CONTENT_PROTECTION_METHOD_NONE;

  std::move(callback).Run(set_hdcp_expectation_);
}

void TestNativeDisplayDelegate::SetColorCalibration(
    int64_t display_id,
    const ColorCalibration& calibration) {
  log_->AppendAction(SetColorCalibrationAction(display_id, calibration));
}

void TestNativeDisplayDelegate::SetColorTemperatureAdjustment(
    int64_t display_id,
    const ColorTemperatureAdjustment& cta) {
  log_->AppendAction(SetColorTemperatureAdjustmentAction(display_id, cta));
}

void TestNativeDisplayDelegate::SetGammaAdjustment(
    int64_t display_id,
    const GammaAdjustment& gamma) {
  log_->AppendAction(SetGammaAdjustmentAction(display_id, gamma));
}

void TestNativeDisplayDelegate::SetPrivacyScreen(
    int64_t display_id,
    bool enabled,
    SetPrivacyScreenCallback callback) {
  log_->AppendAction(SetPrivacyScreenAction(display_id, enabled));
  std::move(callback).Run(true);
}

void TestNativeDisplayDelegate::GetSeamlessRefreshRates(
    int64_t display_id,
    GetSeamlessRefreshRatesCallback callback) const {
  const DisplaySnapshot* snapshot = nullptr;
  for (const auto& output : outputs_) {
    if (output->display_id() == display_id) {
      snapshot = output.get();
      break;
    }
  }
  // Return nullopt if there is no snapshot with this display_id.
  std::optional<std::vector<float>> result;
  if (snapshot) {
    // Return empty vector if there is no current mode.
    std::vector<float> refresh_rates;
    if (snapshot->current_mode()) {
      for (auto& mode : snapshot->modes()) {
        // If a mode has the same size as the currently configured mode, then
        // include that mode's refresh rate.
        if (mode->size() == snapshot->current_mode()->size()) {
          refresh_rates.push_back(mode->refresh_rate());
        }
      }
    }
    result.emplace(refresh_rates);
  }

  if (run_async_) {
    base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
        FROM_HERE, base::BindOnce(std::move(callback), result));
  } else {
    std::move(callback).Run(result);
  }
}

void TestNativeDisplayDelegate::AddObserver(NativeDisplayObserver* observer) {
  observers_.AddObserver(observer);
}

void TestNativeDisplayDelegate::RemoveObserver(
    NativeDisplayObserver* observer) {
  observers_.RemoveObserver(observer);
}

FakeDisplayController* TestNativeDisplayDelegate::GetFakeDisplayController() {
  return nullptr;
}

}  // namespace display::test