blob: cca6d9bc8920b85cb0a1a6f4ca1a6ad124d6e8de [file] [log] [blame]
// Copyright 2018 The Chromium OS 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 "cecservice/cec_manager.h"
#include <algorithm>
#include <string>
#include <utility>
#include <vector>
#include <base/bind.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/strings/string_util.h>
namespace cecservice {
namespace {
std::string PowerStatusToString(TvPowerStatus status) {
switch (status) {
case kTvPowerStatusError:
return "error";
case kTvPowerStatusAdapterNotConfigured:
return "adapter not configured";
case kTvPowerStatusNoTv:
return "no TV";
case kTvPowerStatusOn:
return "on";
case kTvPowerStatusStandBy:
return "standby";
case kTvPowerStatusToOn:
return "to on";
case kTvPowerStatusToStandBy:
return "to standby";
case kTvPowerStatusUnknown:
return "unknown";
}
}
std::string PowerStatusVectorToString(
const std::vector<TvPowerStatus>& vector) {
std::vector<std::string> strings;
std::transform(vector.begin(), vector.end(), std::back_inserter(strings),
PowerStatusToString);
return "[" + base::JoinString(strings, ", ") + "]";
}
} // namespace
struct CecManager::TvPowerStatusResult {
// Set to true if the response has been received from the device.
bool received = false;
// The actual power status received.
TvPowerStatus power_status = kTvPowerStatusUnknown;
};
struct CecManager::TvsPowerStatusQuery {
// Callback to invoke when all responses have been received.
GetTvsPowerStatusCallback callback;
// Device nodes the request has been sent to.
std::map<base::FilePath, TvPowerStatusResult> responses;
};
CecManager::CecManager(const UdevFactory& udev_factory,
const CecDeviceFactory& cec_factory)
: cec_factory_(cec_factory) {
udev_ = udev_factory.Create(
base::Bind(&CecManager::OnDeviceAdded, weak_factory_.GetWeakPtr()),
base::Bind(&CecManager::OnDeviceRemoved, weak_factory_.GetWeakPtr()));
LOG_IF(FATAL, !udev_) << "Failed to create udev";
EnumerateAndAddExistingDevices();
}
CecManager::~CecManager() = default;
void CecManager::GetTvsPowerStatus(GetTvsPowerStatusCallback callback) {
LOG(INFO) << "Received get TVs power status request";
if (devices_.empty()) {
std::move(callback).Run({});
return;
}
TvsPowerStatusQuery query{std::move(callback)};
for (auto& kv : devices_) {
query.responses.insert(std::make_pair(kv.first, TvPowerStatusResult()));
}
QueryId id = next_query_id_++;
tv_power_status_queries_.insert(std::make_pair(id, std::move(query)));
for (auto& kv : devices_) {
kv.second->GetTvPowerStatus(base::Bind(&CecManager::OnTvPowerResponse,
weak_factory_.GetWeakPtr(), id,
kv.first));
}
}
void CecManager::SetWakeUp() {
LOG(INFO) << "Received wake up request";
for (auto& kv : devices_) {
kv.second->SetWakeUp();
}
}
void CecManager::SetStandBy() {
LOG(INFO) << "Received standby request";
for (auto& kv : devices_) {
kv.second->SetStandBy();
}
}
void CecManager::OnTvPowerResponse(QueryId id,
base::FilePath device_path,
TvPowerStatus result) {
auto iterator = tv_power_status_queries_.find(id);
CHECK(iterator != tv_power_status_queries_.end());
TvsPowerStatusQuery& query = iterator->second;
query.responses[device_path] = {true, result};
if (MaybeRespondToTvsPowerStatusQuery(query)) {
tv_power_status_queries_.erase(iterator);
}
}
bool CecManager::MaybeRespondToTvsPowerStatusQuery(
const TvsPowerStatusQuery& query) {
std::vector<TvPowerStatus> result;
for (auto& response : query.responses) {
const TvPowerStatusResult& status = response.second;
if (!status.received) {
return false;
}
result.push_back(status.power_status);
}
LOG(INFO) << "Responding to power status request with: "
<< PowerStatusVectorToString(result);
std::move(query.callback).Run(result);
return true;
}
void CecManager::OnDeviceAdded(const base::FilePath& device_path) {
LOG(INFO) << "New device: " << device_path.value();
AddNewDevice(device_path);
}
void CecManager::OnDeviceRemoved(const base::FilePath& device_path) {
LOG(INFO) << "Removing device: " << device_path.value();
devices_.erase(device_path);
auto iterator = tv_power_status_queries_.begin();
while (iterator != tv_power_status_queries_.end()) {
TvsPowerStatusQuery& query = iterator->second;
query.responses.erase(device_path);
if (MaybeRespondToTvsPowerStatusQuery(query)) {
iterator = tv_power_status_queries_.erase(iterator);
} else {
++iterator;
}
}
}
void CecManager::EnumerateAndAddExistingDevices() {
std::vector<base::FilePath> paths;
if (!udev_->EnumerateDevices(&paths)) {
LOG(FATAL) << "Failed to enumerate devices.";
}
for (const auto& path : paths) {
AddNewDevice(path);
}
}
void CecManager::AddNewDevice(const base::FilePath& path) {
std::unique_ptr<CecDevice> device = cec_factory_.Create(path);
if (device) {
LOG(INFO) << "Added new device: " << path.value();
devices_[path] = std::move(device);
} else {
LOG(WARNING) << "Failed to add device: " << path.value();
}
}
} // namespace cecservice