// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_WEBUI_DIAGNOSTICS_UI_BACKEND_INPUT_INPUT_DATA_PROVIDER_H_
#define ASH_WEBUI_DIAGNOSTICS_UI_BACKEND_INPUT_INPUT_DATA_PROVIDER_H_
#include "ash/accelerators/accelerator_controller_impl.h"
#include "ash/public/cpp/tablet_mode_observer.h"
#include "ash/system/diagnostics/mojom/input.mojom.h"
#include "ash/webui/diagnostics_ui/backend/input/event_watcher_factory.h"
#include "ash/webui/diagnostics_ui/backend/input/healthd_event_reporter.h"
#include "ash/webui/diagnostics_ui/backend/input/input_data_event_watcher.h"
#include "ash/webui/diagnostics_ui/backend/input/input_data_provider_keyboard.h"
#include "ash/webui/diagnostics_ui/backend/input/input_data_provider_touch.h"
#include "ash/webui/diagnostics_ui/backend/input/input_device_information.h"
#include "ash/webui/diagnostics_ui/backend/input/keyboard_input_data_event_watcher.h"
#include "ash/webui/diagnostics_ui/mojom/input_data_provider.mojom.h"
#include "base/containers/flat_map.h"
#include "base/files/file_path.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/weak_ptr.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/sequence_bound.h"
#include "chromeos/dbus/power/power_manager_client.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote_set.h"
#include "ui/aura/window.h"
#include "ui/display/manager/display_configurator.h"
#include "ui/display/types/display_constants.h"
#include "ui/events/ash/event_rewriter_ash.h"
#include "ui/events/ozone/device/device_event.h"
#include "ui/events/ozone/device/device_event_observer.h"
#include "ui/events/ozone/device/device_manager.h"
#include "ui/events/ozone/evdev/event_device_info.h"
#include "ui/views/widget/widget.h"
#include "ui/views/widget/widget_observer.h"
namespace ash::diagnostics {
// Provides information about input devices connected to the system. Implemented
// in the browser process, constructed within the Diagnostics_UI in the browser
// process, and eventually called by the Diagnostics SWA (a renderer process).
class InputDataProvider : public mojom::InputDataProvider,
public ui::DeviceEventObserver,
public KeyboardInputDataEventWatcher::Dispatcher,
public views::WidgetObserver,
public TabletModeObserver,
public display::DisplayConfigurator::Observer,
public chromeos::PowerManagerClient::Observer {
public:
explicit InputDataProvider(aura::Window* window);
explicit InputDataProvider(
aura::Window* window,
std::unique_ptr<ui::DeviceManager> device_manager,
std::unique_ptr<EventWatcherFactory> watcher_factory,
AcceleratorControllerImpl* accelerator_controller,
ui::EventRewriterAsh::Delegate* event_rewriter_delegate);
InputDataProvider(const InputDataProvider&) = delete;
InputDataProvider& operator=(const InputDataProvider&) = delete;
~InputDataProvider() override;
static bool ShouldCloseDialogOnEscape() {
return should_close_dialog_on_escape_;
}
void BindInterface(
mojo::PendingReceiver<mojom::InputDataProvider> pending_receiver);
static mojom::ConnectionType ConnectionTypeFromInputDeviceType(
ui::InputDeviceType type);
// KeyboardInputDataEventWatcher::Dispatcher:
void SendInputKeyEvent(uint32_t id,
uint32_t key_code,
uint32_t scan_code,
bool down) override;
// mojom::InputDataProvider:
void GetConnectedDevices(GetConnectedDevicesCallback callback) override;
void ObserveConnectedDevices(
mojo::PendingRemote<mojom::ConnectedDevicesObserver> observer) override;
void ObserveKeyEvents(
uint32_t id,
mojo::PendingRemote<mojom::KeyboardObserver> observer) override;
void ObserveTabletMode(
mojo::PendingRemote<mojom::TabletModeObserver> observer,
ObserveTabletModeCallback callback) override;
void ObserveLidState(mojo::PendingRemote<mojom::LidStateObserver> observer,
ObserveLidStateCallback callback) override;
void ObserveInternalDisplayPowerState(
mojo::PendingRemote<mojom::InternalDisplayPowerStateObserver> observer)
override;
void MoveAppToTestingScreen(uint32_t evdev_id) override;
void MoveAppBackToPreviousScreen() override;
void SetA11yTouchPassthrough(bool enabled) override;
// ui::DeviceEventObserver:
void OnDeviceEvent(const ui::DeviceEvent& event) override;
// views::WidgetObserver:
void OnWidgetVisibilityChanged(views::Widget* widget, bool visible) override;
void OnWidgetActivationChanged(views::Widget* widget, bool active) override;
// TabletModeObserver:
void OnTabletModeEventsBlockingChanged() override;
// chromeos::PowerManagerClient::Observer
void LidEventReceived(chromeos::PowerManagerClient::LidState state,
base::TimeTicks time) override;
void OnReceiveSwitchStates(
std::optional<chromeos::PowerManagerClient::SwitchStates> switch_states);
// display::DisplayConfigurator::Observer
void OnPowerStateChanged(chromeos::DisplayPowerState power_state) override;
// Get the value of is_internal_display_on_ for testing purpose.
bool is_internal_display_on() { return is_internal_display_on_; }
protected:
base::SequenceBound<InputDeviceInfoHelper> info_helper_{
base::ThreadPool::CreateSequencedTaskRunner({base::MayBlock()})};
private:
void Initialize(aura::Window* window);
void GetConnectedDevicesHelper(GetConnectedDevicesCallback callback);
void ProcessDeviceInfo(std::unique_ptr<InputDeviceInformation> device_info);
void AddTouchDevice(const InputDeviceInformation* device_info);
void AddKeyboard(const InputDeviceInformation* device_info);
bool may_send_events_ = false;
// Review widget state to determine whether it is safe to send events.
void UpdateMaySendEvents();
// Pass on pause or resume events to observers if that state has changed.
void UpdateEventObservers();
void SendPauseEvents();
void SendResumeEvents();
void BlockShortcuts(bool should_block);
InputDataProviderKeyboard keyboard_helper_;
InputDataProviderTouch touch_helper_;
// Handle destroyed KeyboardObservers.
void OnObservedKeyboardInputDisconnect(uint32_t evdev_id,
mojo::RemoteSetElementId observer_id);
// Manage watchers that read an evdev and process events for observers.
void ForwardKeyboardInput(uint32_t id);
void UnforwardKeyboardInput(uint32_t id);
const std::string GetKeyboardName(uint32_t id);
// Denotes whether DiagnosticsDialog should be closed when escape is pressed.
// Currently, this is only false when the keyboard tester is actively in use.
static bool should_close_dialog_on_escape_;
// Whether a tablet mode switch is present (which we use as a hint for the
// top-right key glyph).
bool has_tablet_mode_switch_ = false;
// Whether the internal touchscreen is on, used to determine if the internal
// display is testable or not.
bool is_internal_display_on_ = true;
// Whether the laptop lid is closed or open. On chromeboxes, this will always
// be false.
bool is_lid_open_ = false;
// Id of the previous display the Diagnostics app was in, used to move the app
// back to previous display when the touchscreen tester is closed.
int64_t previous_display_id_ = display::kInvalidDisplayId;
// Map by evdev ids to information blocks.
base::flat_map<int, mojom::KeyboardInfoPtr> keyboards_;
base::flat_map<int, std::unique_ptr<InputDataProviderKeyboard::AuxData>>
keyboard_aux_data_;
base::flat_map<int, mojom::TouchDeviceInfoPtr> touch_devices_;
// Map by evdev ids to remote observers and event watchers.
base::flat_map<int, std::unique_ptr<mojo::RemoteSet<mojom::KeyboardObserver>>>
keyboard_observers_;
base::flat_map<int, std::unique_ptr<InputDataEventWatcher>>
keyboard_watchers_;
// Timestamp of when keyboard tester is first opened. Undefined if the
// keyboard tester is not open.
base::Time keyboard_tester_start_timestamp_;
bool logged_not_dispatching_key_events_ = false;
raw_ptr<views::Widget> widget_ = nullptr;
mojo::RemoteSet<mojom::ConnectedDevicesObserver> connected_devices_observers_;
mojo::RemoteSet<mojom::TabletModeObserver> tablet_mode_observers_;
mojo::RemoteSet<mojom::LidStateObserver> lid_state_observers_;
mojo::Remote<mojom::InternalDisplayPowerStateObserver>
internal_display_power_state_observer_;
mojo::Receiver<mojom::InputDataProvider> receiver_{this};
std::unique_ptr<ui::DeviceManager> device_manager_;
std::unique_ptr<EventWatcherFactory> watcher_factory_;
raw_ptr<AcceleratorControllerImpl> accelerator_controller_;
raw_ptr<ui::EventRewriterAsh::Delegate> event_rewriter_delegate_;
HealthdEventReporter healthd_event_reporter_;
base::OnceCallback<void()> get_connected_devices_callback_;
base::WeakPtrFactory<InputDataProvider> weak_factory_{this};
};
} // namespace ash::diagnostics
#endif // ASH_WEBUI_DIAGNOSTICS_UI_BACKEND_INPUT_INPUT_DATA_PROVIDER_H_