// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/services/cros_healthd/private/cpp/data_collector.h"
#include <fcntl.h>
#include "ash/display/privacy_screen_controller.h"
#include "ash/shell.h"
#include "base/check_op.h"
#include "base/files/file_enumerator.h"
#include "base/no_destructor.h"
#include "base/notreached.h"
#include "base/posix/eintr_wrapper.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "chromeos/ash/components/mojo_service_manager/connection.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "third_party/cros_system_api/mojo/service_constants.h"
#include "ui/events/devices/device_data_manager.h"
#include "ui/events/ozone/evdev/event_device_info.h"
namespace ash::cros_healthd::internal {
namespace {
class DataCollectorDelegateImpl : public DataCollector::Delegate {
public:
DataCollectorDelegateImpl();
DataCollectorDelegateImpl(const DataCollectorDelegateImpl&) = delete;
DataCollectorDelegateImpl& operator=(const DataCollectorDelegateImpl&) =
delete;
private:
~DataCollectorDelegateImpl() override;
// DataCollector::Delegate override.
std::string GetTouchpadLibraryName() override;
bool IsPrivacyScreenSupported() override;
bool IsPrivacyScreenManaged() override;
void SetPrivacyScreenState(bool state) override;
};
DataCollectorDelegateImpl::DataCollectorDelegateImpl() = default;
DataCollectorDelegateImpl::~DataCollectorDelegateImpl() = default;
std::string DataCollectorDelegateImpl::GetTouchpadLibraryName() {
#if defined(USE_LIBINPUT)
base::FileEnumerator file_enum(base::FilePath("/dev/input/"), false,
base::FileEnumerator::FileType::FILES);
for (auto path = file_enum.Next(); !path.empty(); path = file_enum.Next()) {
base::ScopedFD fd(
HANDLE_EINTR(open(path.value().c_str(), O_RDWR | O_NONBLOCK)));
if (fd.get() < 0) {
LOG(ERROR) << "Couldn't open device path " << path;
continue;
}
auto devinfo = std::make_unique<ui::EventDeviceInfo>();
if (!devinfo->Initialize(fd.get(), path)) {
LOG(ERROR) << "Failed to get device info for " << path;
continue;
}
if (!devinfo->HasTouchpad() ||
devinfo->device_type() != ui::InputDeviceType::INPUT_DEVICE_INTERNAL) {
continue;
}
if (devinfo->UseLibinput()) {
return "libinput";
}
}
#endif
#if defined(USE_EVDEV_GESTURES)
return "gestures";
#else
return "Default EventConverterEvdev";
#endif
}
bool DataCollectorDelegateImpl::IsPrivacyScreenSupported() {
return Shell::Get()->privacy_screen_controller()->IsSupported();
}
bool DataCollectorDelegateImpl::IsPrivacyScreenManaged() {
return Shell::Get()->privacy_screen_controller()->IsManaged();
}
void DataCollectorDelegateImpl::SetPrivacyScreenState(bool state) {
Shell::Get()->privacy_screen_controller()->SetEnabled(state);
}
DataCollectorDelegateImpl* GetDataCollectorDelegate() {
static base::NoDestructor<DataCollectorDelegateImpl> delegate;
return delegate.get();
}
mojom::InputDevice::ConnectionType GetInputDeviceConnectionType(
ui::InputDeviceType type) {
switch (type) {
case ui::INPUT_DEVICE_INTERNAL:
return mojom::InputDevice::ConnectionType::kInternal;
case ui::INPUT_DEVICE_USB:
return mojom::InputDevice::ConnectionType::kUSB;
case ui::INPUT_DEVICE_BLUETOOTH:
return mojom::InputDevice::ConnectionType::kBluetooth;
case ui::INPUT_DEVICE_UNKNOWN:
return mojom::InputDevice::ConnectionType::kUnknown;
}
}
void GetTouchscreenDevicesOnUIThread(
scoped_refptr<base::SequencedTaskRunner> task_runner,
DataCollector::GetTouchscreenDevicesCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
const std::vector<ui::TouchscreenDevice>& devices =
ui::DeviceDataManager::GetInstance()->GetTouchscreenDevices();
std::vector<mojom::TouchscreenDevicePtr> results;
for (const auto& device : devices) {
auto result = mojom::TouchscreenDevice::New();
result->input_device = mojom::InputDevice::New();
result->input_device->name = device.name;
result->input_device->connection_type =
GetInputDeviceConnectionType(device.type);
result->input_device->physical_location = device.phys;
result->input_device->is_enabled = device.enabled;
result->input_device->sysfs_path = device.sys_path.value();
result->touch_points = device.touch_points;
result->has_stylus = device.has_stylus;
result->has_stylus_garage_switch = device.has_stylus_garage_switch;
results.push_back(std::move(result));
}
task_runner->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), std::move(results)));
}
} // namespace
DataCollector::DataCollector() : DataCollector(GetDataCollectorDelegate()) {
if (mojo_service_manager::IsServiceManagerBound()) {
mojo_service_manager::GetServiceManagerProxy()->Register(
chromeos::mojo_services::kChromiumCrosHealthdDataCollector,
provider_receiver_.BindNewPipeAndPassRemote());
}
}
DataCollector::DataCollector(Delegate* delegate) : delegate_(delegate) {}
DataCollector::~DataCollector() = default;
mojo::PendingRemote<mojom::ChromiumDataCollector>
DataCollector::BindNewPipeAndPassRemote() {
mojo::PendingRemote<mojom::ChromiumDataCollector> remote;
receiver_set_.Add(this, remote.InitWithNewPipeAndPassReceiver());
return remote;
}
void DataCollector::GetTouchscreenDevices(
GetTouchscreenDevicesCallback callback) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&GetTouchscreenDevicesOnUIThread,
base::SequencedTaskRunner::GetCurrentDefault(),
std::move(callback)));
}
void DataCollector::GetTouchpadLibraryName(
GetTouchpadLibraryNameCallback callback) {
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::BindOnce(&Delegate::GetTouchpadLibraryName,
base::Unretained(delegate_)),
std::move(callback));
}
void DataCollector::SetPrivacyScreenState(
bool state,
SetPrivacyScreenStateCallback callback) {
if (!delegate_->IsPrivacyScreenSupported() ||
delegate_->IsPrivacyScreenManaged()) {
std::move(callback).Run(false);
return;
}
delegate_->SetPrivacyScreenState(state);
std::move(callback).Run(true);
}
void DataCollector::DEPRECATED_SetAudioOutputMute(
bool mute_on,
DEPRECATED_SetAudioOutputMuteCallback callback) {
std::move(callback).Run(/*success*/ false);
}
void DataCollector::Request(
chromeos::mojo_service_manager::mojom::ProcessIdentityPtr identity,
mojo::ScopedMessagePipeHandle receiver) {
receiver_set_.Add(this, mojo::PendingReceiver<mojom::ChromiumDataCollector>(
std::move(receiver)));
}
} // namespace ash::cros_healthd::internal