#include "ui/display/manager/display_port_observer.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/task/thread_pool.h"
#include "base/threading/scoped_blocking_call.h"
#include "third_party/re2/src/re2/re2.h"
#include "ui/display/types/display_snapshot.h"
namespace display {
namespace {
const LazyRE2 kTypecConnUeventPattern = {R"(TYPEC_PORT=port(\d+))"};
std::vector<uint32_t> ParseDrmSysfsAndFindPort(
const std::vector<std::pair<uint64_t, base::FilePath>>&
base_connector_id_and_syspath) {
base::ScopedBlockingCall scoped_blocking_call(FROM_HERE,
base::BlockingType::MAY_BLOCK);
std::vector<uint32_t> port_nums;
for (const auto& pair : base_connector_id_and_syspath) {
auto base_connector_id = pair.first;
const auto& sys_path = pair.second;
base::FileEnumerator enumerator(sys_path, false,
base::FileEnumerator::DIRECTORIES,
FILE_PATH_LITERAL("card*-DP-*"));
for (auto path = enumerator.Next(); !path.empty();
path = enumerator.Next()) {
std::string connector_id_str;
uint64_t connector_id_int;
if (!base::ReadFileToString(path.Append("connector_id"),
&connector_id_str)) {
continue;
}
base::TrimWhitespaceASCII(connector_id_str, base::TRIM_ALL,
&connector_id_str);
if (!base::StringToUint64(connector_id_str, &connector_id_int)) {
LOG(WARNING) << "Invalid connector id " << connector_id_str << " at "
<< path.value();
continue;
}
if (connector_id_int != base_connector_id) {
continue;
}
std::string typec_conn_uevent;
uint32_t port_num;
if (!base::ReadFileToString(path.Append("typec_connector/uevent"),
&typec_conn_uevent)) {
break;
}
if (!RE2::PartialMatch(typec_conn_uevent, *kTypecConnUeventPattern,
&port_num)) {
break;
}
port_nums.push_back(port_num);
break;
}
}
return port_nums;
}
}
DisplayPortObserver::DisplayPortObserver(
DisplayConfigurator* configurator,
base::RepeatingCallback<void(const std::vector<uint32_t>&)>
on_port_change_callback)
: configurator_(configurator),
on_port_change_callback_(on_port_change_callback) {
if (configurator_) {
configurator_->AddObserver(this);
}
}
DisplayPortObserver::~DisplayPortObserver() {
if (configurator_) {
configurator_->RemoveObserver(this);
}
}
void DisplayPortObserver::OnDisplayConfigurationChanged(
const DisplayConfigurator::DisplayStateList& display_states) {
std::set<uint64_t> base_connector_ids_;
for (display::DisplaySnapshot* state : display_states) {
base_connector_ids_.insert(state->base_connector_id());
}
if (base_connector_ids_ == prev_base_connector_ids_) {
return;
}
prev_base_connector_ids_ = base_connector_ids_;
std::vector<std::pair<uint64_t, base::FilePath>>
base_connector_id_and_syspath;
for (display::DisplaySnapshot* state : display_states) {
base_connector_id_and_syspath.push_back(
std::make_pair(state->base_connector_id(), state->sys_path()));
}
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE,
{base::MayBlock(), base::TaskPriority::BEST_EFFORT,
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN},
base::BindOnce(&ParseDrmSysfsAndFindPort, base_connector_id_and_syspath),
base::BindOnce(&DisplayPortObserver::SetTypeCPortsUsingDisplays,
weak_ptr_factory_.GetWeakPtr()));
}
void DisplayPortObserver::OnDisplayConfigurationChangeFailed(
const DisplayConfigurator::DisplayStateList& displays,
MultipleDisplayState failed_new_state) {}
void DisplayPortObserver::SetTypeCPortsUsingDisplays(
std::vector<uint32_t> port_nums) {
on_port_change_callback_.Run(port_nums);
}
}