// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/capture/video/chromeos/system_event_monitor_impl.h"
#include "base/system/system_monitor.h"
#include "base/task/bind_post_task.h"
#include "base/unguessable_token.h"
#include "chromeos/ash/components/mojo_service_manager/connection.h"
#include "third_party/cros_system_api/mojo/service_constants.h"
namespace media {
namespace {
cros::mojom::LidState ConvertLidState(
chromeos::PowerManagerClient::LidState state) {
switch (state) {
case chromeos::PowerManagerClient::LidState::OPEN:
return cros::mojom::LidState::kOpen;
case chromeos::PowerManagerClient::LidState::CLOSED:
return cros::mojom::LidState::kClosed;
case chromeos::PowerManagerClient::LidState::NOT_PRESENT:
return cros::mojom::LidState::kNotPresent;
}
return cros::mojom::LidState::kNotPresent;
}
cros::mojom::ClockwiseRotation ConvertRotation(
display::Display::Rotation rotation) {
switch (rotation) {
case display::Display::Rotation::ROTATE_0:
return cros::mojom::ClockwiseRotation::kRotate0;
case display::Display::Rotation::ROTATE_90:
return cros::mojom::ClockwiseRotation::kRotate90;
case display::Display::Rotation::ROTATE_180:
return cros::mojom::ClockwiseRotation::kRotate180;
case display::Display::Rotation::ROTATE_270:
return cros::mojom::ClockwiseRotation::kRotate270;
}
return cros::mojom::ClockwiseRotation::kRotate0;
}
} // namespace
SystemEventMonitorImpl::SystemEventMonitorImpl() {
CHECK(ash::mojo_service_manager::IsServiceManagerBound());
auto* proxy = ash::mojo_service_manager::GetServiceManagerProxy();
proxy->Register(
/*service_name=*/chromeos::mojo_services::kCrosSystemEventMonitor,
provider_receiver_.BindNewPipeAndPassRemote());
lid_observers_.set_disconnect_handler(base::BindRepeating(
&SystemEventMonitorImpl::RemoveLidObserver, weak_factory_.GetWeakPtr()));
chromeos::PowerManagerClient* power_manager_client =
chromeos::PowerManagerClient::Get();
if (!power_manager_client) {
// power_manager_client may be NULL in unittests.
return;
}
power_manager_client->AddObserver(this);
}
SystemEventMonitorImpl::~SystemEventMonitorImpl() {
chromeos::PowerManagerClient* power_manager_client =
chromeos::PowerManagerClient::Get();
// power_manager_client may be NULL in unittests.
if (!power_manager_client) {
return;
}
power_manager_client->RemoveObserver(this);
}
void SystemEventMonitorImpl::Request(
chromeos::mojo_service_manager::mojom::ProcessIdentityPtr identity,
mojo::ScopedMessagePipeHandle receiver) {
receiver_set_.Add(this,
mojo::PendingReceiver<cros::mojom::CrosSystemEventMonitor>(
std::move(receiver)));
}
void SystemEventMonitorImpl::AddDisplayObserver(
mojo::PendingRemote<cros::mojom::CrosDisplayObserver> observer) {
std::unique_ptr<DisplayObserver> display_observer =
std::make_unique<DisplayObserver>(
std::move(observer),
base::BindOnce(&SystemEventMonitorImpl::RemoveDisplayObserver,
weak_factory_.GetWeakPtr()));
display_observers_.emplace(display_observer.get(),
std::move(display_observer));
}
void SystemEventMonitorImpl::AddLidObserver(
mojo::PendingRemote<cros::mojom::CrosLidObserver> observer) {
auto id = lid_observers_.Add(std::move(observer));
lid_observers_.Get(id)->OnLidStateChanged(lid_state_);
}
void SystemEventMonitorImpl::AddPowerObserver(
const std::string& info,
mojo::PendingRemote<cros::mojom::CrosPowerObserver> observer) {
std::unique_ptr<PowerObserver> power_observer =
std::make_unique<PowerObserver>(
info, std::move(observer),
base::BindOnce(&SystemEventMonitorImpl::RemovePowerObserver,
weak_factory_.GetWeakPtr()));
power_observers_.emplace(power_observer.get(), std::move(power_observer));
}
void SystemEventMonitorImpl::NotifyDeviceChanged(cros::mojom::DeviceType type) {
base::SystemMonitor* monitor = base::SystemMonitor::Get();
// |monitor| might be nullptr in unittest.
if (!monitor) {
return;
}
base::SystemMonitor::DeviceType monitor_type =
base::SystemMonitor::DeviceType::DEVTYPE_UNKNOWN;
switch (type) {
case cros::mojom::DeviceType::kAudio:
monitor_type = base::SystemMonitor::DeviceType::DEVTYPE_AUDIO;
break;
case cros::mojom::DeviceType::kVideoCapture:
monitor_type = base::SystemMonitor::DeviceType::DEVTYPE_VIDEO_CAPTURE;
break;
case cros::mojom::DeviceType::kUnkown:
monitor_type = base::SystemMonitor::DeviceType::DEVTYPE_UNKNOWN;
break;
}
monitor->ProcessDevicesChanged(monitor_type);
}
void SystemEventMonitorImpl::LidEventReceived(
chromeos::PowerManagerClient::LidState state,
base::TimeTicks timestamp) {
cros::mojom::LidState new_state = ConvertLidState(state);
if (lid_state_ == new_state) {
return;
}
lid_state_ = new_state;
for (auto& observer : lid_observers_) {
observer->OnLidStateChanged(new_state);
}
}
void SystemEventMonitorImpl::RemoveDisplayObserver(DisplayObserver* observer) {
display_observers_.erase(observer);
}
void SystemEventMonitorImpl::RemoveLidObserver(mojo::RemoteSetElementId id) {
lid_observers_.Remove(id);
}
void SystemEventMonitorImpl::RemovePowerObserver(PowerObserver* observer) {
power_observers_.erase(observer);
}
SystemEventMonitorImpl::DisplayObserver::DisplayObserver(
mojo::PendingRemote<cros::mojom::CrosDisplayObserver> observer,
base::OnceCallback<void(DisplayObserver*)> cleanup_callback)
: display_remote_observer_(std::move(observer)) {
display_remote_observer_.set_disconnect_handler(base::BindOnce(
&DisplayObserver::OnRemoteObserverDisconnected,
weak_ptr_factory_.GetWeakPtr(), std::move(cleanup_callback)));
// |display::Screen| is not ready when |SystemEventMonitorImpl| initializes.
// Therefore, we defer the observation and assume that |display::Screen| is
// ready when |SystemEventMonitorImpl::AddDisplayObserver| is invoked and let
// |DisplayObserver| observe it.
display::Screen* screen = display::Screen::GetScreen();
if (!screen) {
// screen may be NULL in unittests.
LOG(WARNING) << "Screen has not been initialized yet.";
return;
}
display::Display display = screen->GetPrimaryDisplay();
if (!display.IsInternal()) {
return;
}
display_remote_observer_->OnDisplayRotationChanged(
ConvertRotation(display.rotation()));
}
SystemEventMonitorImpl::DisplayObserver::~DisplayObserver() = default;
void SystemEventMonitorImpl::DisplayObserver::OnDisplayMetricsChanged(
const display::Display& display,
uint32_t metrics) {
if (!(metrics & DISPLAY_METRIC_ROTATION)) {
return;
}
if (!display.IsInternal()) {
return;
}
display_remote_observer_->OnDisplayRotationChanged(
ConvertRotation(display.rotation()));
}
void SystemEventMonitorImpl::DisplayObserver::OnRemoteObserverDisconnected(
base::OnceCallback<void(DisplayObserver*)> cleanup_callback) {
std::move(cleanup_callback).Run(this);
}
SystemEventMonitorImpl::PowerObserver::PowerObserver(
const std::string& client_name,
mojo::PendingRemote<cros::mojom::CrosPowerObserver> observer,
base::OnceCallback<void(PowerObserver*)> cleanup_callback)
: client_name_(client_name), power_remote_observer_(std::move(observer)) {
power_remote_observer_.set_disconnect_handler(base::BindOnce(
&PowerObserver::OnRemoteObserverDisconnected,
weak_ptr_factory_.GetWeakPtr(), std::move(cleanup_callback)));
chromeos::PowerManagerClient* power_manager_client =
chromeos::PowerManagerClient::Get();
if (!power_manager_client) {
// power_manager_client may be NULL in unittests.
return;
}
power_manager_client->AddObserver(this);
}
SystemEventMonitorImpl::PowerObserver::~PowerObserver() {
chromeos::PowerManagerClient* power_manager_client =
chromeos::PowerManagerClient::Get();
if (!power_manager_client) {
// power_manager_client may be NULL in unittests.
return;
}
for (auto& token : block_suspend_tokens_) {
chromeos::PowerManagerClient::Get()->UnblockSuspend(token);
}
power_manager_client->RemoveObserver(this);
}
void SystemEventMonitorImpl::PowerObserver::SuspendImminent(
power_manager::SuspendImminent::Reason reason) {
base::UnguessableToken token = base::UnguessableToken::Create();
chromeos::PowerManagerClient::Get()->BlockSuspend(token, client_name_);
power_remote_observer_->OnSystemSuspend(base::BindPostTaskToCurrentDefault(
base::BindOnce(&PowerObserver::UnblockSuspend,
weak_ptr_factory_.GetWeakPtr(), token)));
block_suspend_tokens_.insert(token);
}
void SystemEventMonitorImpl::PowerObserver::SuspendDone(
base::TimeDelta sleep_duration) {
power_remote_observer_->OnSystemResume();
}
void SystemEventMonitorImpl::PowerObserver::UnblockSuspend(
base::UnguessableToken token) {
chromeos::PowerManagerClient::Get()->UnblockSuspend(token);
block_suspend_tokens_.erase(token);
}
void SystemEventMonitorImpl::PowerObserver::OnRemoteObserverDisconnected(
base::OnceCallback<void(PowerObserver*)> cleanup_callback) {
std::move(cleanup_callback).Run(this);
}
} // namespace media