| // Copyright (c) 2012 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 "power_manager/powerd/policy/input_event_handler.h" |
| |
| #include <utility> |
| |
| #include <base/check_op.h> |
| #include <base/logging.h> |
| #include <chromeos/dbus/service_constants.h> |
| #include <dbus/message.h> |
| |
| #include "power_manager/common/clock.h" |
| #include "power_manager/common/power_constants.h" |
| #include "power_manager/common/prefs.h" |
| #include "power_manager/common/util.h" |
| #include "power_manager/powerd/system/dbus_wrapper.h" |
| #include "power_manager/powerd/system/display/display_watcher.h" |
| #include "power_manager/powerd/system/input_watcher_interface.h" |
| #include "power_manager/proto_bindings/input_event.pb.h" |
| #include "power_manager/proto_bindings/switch_states.pb.h" |
| |
| namespace power_manager { |
| namespace policy { |
| |
| InputEventHandler::InputEventHandler() |
| : clock_(std::make_unique<Clock>()), weak_ptr_factory_(this) {} |
| |
| InputEventHandler::~InputEventHandler() { |
| if (input_watcher_) |
| input_watcher_->RemoveObserver(this); |
| } |
| |
| void InputEventHandler::Init(system::InputWatcherInterface* input_watcher, |
| Delegate* delegate, |
| system::DisplayWatcherInterface* display_watcher, |
| system::DBusWrapperInterface* dbus_wrapper, |
| PrefsInterface* prefs) { |
| input_watcher_ = input_watcher; |
| input_watcher_->AddObserver(this); |
| delegate_ = delegate; |
| display_watcher_ = display_watcher; |
| |
| dbus_wrapper_ = dbus_wrapper; |
| dbus_wrapper_->ExportMethod( |
| kHandlePowerButtonAcknowledgmentMethod, |
| base::Bind( |
| &InputEventHandler::OnHandlePowerButtonAcknowledgmentMethodCall, |
| weak_ptr_factory_.GetWeakPtr())); |
| dbus_wrapper_->ExportMethod( |
| kIgnoreNextPowerButtonPressMethod, |
| base::Bind(&InputEventHandler::OnIgnoreNextPowerButtonPressMethodCall, |
| weak_ptr_factory_.GetWeakPtr())); |
| dbus_wrapper_->ExportMethod( |
| kGetSwitchStatesMethod, |
| base::Bind(&InputEventHandler::OnGetSwitchStatesMethodCall, |
| weak_ptr_factory_.GetWeakPtr())); |
| |
| prefs->GetBool(kExternalDisplayOnlyPref, &only_has_external_display_); |
| prefs->GetBool(kFactoryModePref, &factory_mode_); |
| |
| bool use_lid = false; |
| if (prefs->GetBool(kUseLidPref, &use_lid) && use_lid) |
| lid_state_ = input_watcher_->QueryLidState(); |
| |
| tablet_mode_ = input_watcher_->GetTabletMode(); |
| } |
| |
| bool InputEventHandler::TriggerPowerButtonAcknowledgmentTimeoutForTesting() { |
| if (!power_button_acknowledgment_timer_.IsRunning()) |
| return false; |
| |
| power_button_acknowledgment_timer_.Stop(); |
| OnPowerButtonAcknowledgmentTimeout(); |
| return true; |
| } |
| |
| void InputEventHandler::OnLidEvent(LidState state) { |
| lid_state_ = state; |
| InputEvent proto; |
| switch (lid_state_) { |
| case LidState::CLOSED: |
| delegate_->HandleLidClosed(); |
| proto.set_type(InputEvent_Type_LID_CLOSED); |
| break; |
| case LidState::OPEN: |
| delegate_->HandleLidOpened(); |
| proto.set_type(InputEvent_Type_LID_OPEN); |
| break; |
| case LidState::NOT_PRESENT: |
| return; |
| } |
| proto.set_timestamp(clock_->GetCurrentTime().ToInternalValue()); |
| dbus_wrapper_->EmitSignalWithProtocolBuffer(kInputEventSignal, proto); |
| } |
| |
| void InputEventHandler::OnTabletModeEvent(TabletMode mode) { |
| DCHECK_NE(mode, TabletMode::UNSUPPORTED); |
| tablet_mode_ = mode; |
| |
| delegate_->HandleTabletModeChange(mode); |
| |
| InputEvent proto; |
| proto.set_type(tablet_mode_ == TabletMode::ON |
| ? InputEvent_Type_TABLET_MODE_ON |
| : InputEvent_Type_TABLET_MODE_OFF); |
| proto.set_timestamp(clock_->GetCurrentTime().ToInternalValue()); |
| dbus_wrapper_->EmitSignalWithProtocolBuffer(kInputEventSignal, proto); |
| } |
| |
| void InputEventHandler::OnPowerButtonEvent(ButtonState state) { |
| if (factory_mode_) { |
| LOG(INFO) << "Ignoring power button " << ButtonStateToString(state) |
| << " for factory mode"; |
| return; |
| } |
| |
| if (clock_->GetCurrentTime() < ignore_power_button_deadline_) { |
| bool ignore = state == ButtonState::DOWN || power_button_down_ignored_; |
| if (state == ButtonState::UP) // Consumed, we no longer need the deadline. |
| IgnoreNextPowerButtonPress(base::TimeDelta()); |
| else if (state == ButtonState::DOWN) |
| power_button_down_ignored_ = true; |
| if (ignore) { |
| // Ignore down event or up event if it matches a down event. |
| LOG(INFO) << "Ignored power button " << ButtonStateToString(state); |
| // Do not forward this event. |
| return; |
| } |
| } |
| |
| if (state == ButtonState::DOWN && only_has_external_display_ && |
| display_watcher_->GetDisplays().empty()) { |
| delegate_->ShutDownForPowerButtonWithNoDisplay(); |
| return; |
| } |
| |
| if (state != ButtonState::REPEAT) { |
| const base::TimeTicks now = clock_->GetCurrentTime(); |
| |
| InputEvent proto; |
| proto.set_type(state == ButtonState::DOWN |
| ? InputEvent_Type_POWER_BUTTON_DOWN |
| : InputEvent_Type_POWER_BUTTON_UP); |
| proto.set_timestamp(now.ToInternalValue()); |
| dbus_wrapper_->EmitSignalWithProtocolBuffer(kInputEventSignal, proto); |
| |
| if (state == ButtonState::DOWN) { |
| expected_power_button_acknowledgment_timestamp_ = now; |
| power_button_acknowledgment_timer_.Start( |
| FROM_HERE, kPowerButtonAcknowledgmentTimeout, this, |
| &InputEventHandler::OnPowerButtonAcknowledgmentTimeout); |
| } else { |
| expected_power_button_acknowledgment_timestamp_ = base::TimeTicks(); |
| power_button_acknowledgment_timer_.Stop(); |
| } |
| } |
| |
| delegate_->HandlePowerButtonEvent(state); |
| } |
| |
| void InputEventHandler::OnHoverStateChange(bool hovering) { |
| delegate_->HandleHoverStateChange(hovering); |
| } |
| |
| void InputEventHandler::IgnoreNextPowerButtonPress( |
| const base::TimeDelta& timeout) { |
| if (timeout.is_zero()) { |
| VLOG(1) << "Cancel power button press discarding"; |
| ignore_power_button_deadline_ = base::TimeTicks(); |
| power_button_down_ignored_ = false; |
| } else { |
| VLOG(1) << "Ignoring power button for " << timeout.InMilliseconds() |
| << " ms"; |
| ignore_power_button_deadline_ = clock_->GetCurrentTime() + timeout; |
| } |
| } |
| |
| void InputEventHandler::OnPowerButtonAcknowledgmentTimeout() { |
| delegate_->ReportPowerButtonAcknowledgmentDelay( |
| kPowerButtonAcknowledgmentTimeout); |
| delegate_->HandleMissingPowerButtonAcknowledgment(); |
| expected_power_button_acknowledgment_timestamp_ = base::TimeTicks(); |
| } |
| |
| void InputEventHandler::OnHandlePowerButtonAcknowledgmentMethodCall( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| int64_t timestamp_internal = 0; |
| dbus::MessageReader reader(method_call); |
| if (!reader.PopInt64(×tamp_internal)) { |
| LOG(ERROR) << "Unable to parse " << kHandlePowerButtonAcknowledgmentMethod |
| << " request"; |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, |
| DBUS_ERROR_INVALID_ARGS, |
| "Expected int64_t timestamp")); |
| return; |
| } |
| |
| const auto timestamp = base::TimeTicks::FromInternalValue(timestamp_internal); |
| VLOG(1) << "Received acknowledgment of power button press at " |
| << timestamp.ToInternalValue() << "; expected " |
| << expected_power_button_acknowledgment_timestamp_.ToInternalValue(); |
| if (timestamp == expected_power_button_acknowledgment_timestamp_) { |
| delegate_->ReportPowerButtonAcknowledgmentDelay( |
| clock_->GetCurrentTime() - |
| expected_power_button_acknowledgment_timestamp_); |
| expected_power_button_acknowledgment_timestamp_ = base::TimeTicks(); |
| power_button_acknowledgment_timer_.Stop(); |
| } |
| std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call)); |
| } |
| |
| void InputEventHandler::OnIgnoreNextPowerButtonPressMethodCall( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| int64_t timeout_internal = 0; |
| dbus::MessageReader reader(method_call); |
| if (!reader.PopInt64(&timeout_internal)) { |
| LOG(ERROR) << "Unable to parse " << kIgnoreNextPowerButtonPressMethod |
| << " request"; |
| std::move(response_sender) |
| .Run(dbus::ErrorResponse::FromMethodCall(method_call, |
| DBUS_ERROR_INVALID_ARGS, |
| "Expected int64_t timestamp")); |
| return; |
| } |
| |
| IgnoreNextPowerButtonPress( |
| base::TimeDelta::FromInternalValue(timeout_internal)); |
| std::move(response_sender).Run(dbus::Response::FromMethodCall(method_call)); |
| } |
| |
| void InputEventHandler::OnGetSwitchStatesMethodCall( |
| dbus::MethodCall* method_call, |
| dbus::ExportedObject::ResponseSender response_sender) { |
| SwitchStates protobuf; |
| switch (input_watcher_->GetTabletMode()) { |
| case TabletMode::ON: |
| protobuf.set_tablet_mode(SwitchStates_TabletMode_ON); |
| break; |
| case TabletMode::OFF: |
| protobuf.set_tablet_mode(SwitchStates_TabletMode_OFF); |
| break; |
| case TabletMode::UNSUPPORTED: |
| protobuf.set_tablet_mode(SwitchStates_TabletMode_UNSUPPORTED); |
| break; |
| } |
| switch (input_watcher_->QueryLidState()) { |
| case LidState::OPEN: |
| protobuf.set_lid_state(SwitchStates_LidState_OPEN); |
| break; |
| case LidState::CLOSED: |
| protobuf.set_lid_state(SwitchStates_LidState_CLOSED); |
| break; |
| case LidState::NOT_PRESENT: |
| protobuf.set_lid_state(SwitchStates_LidState_NOT_PRESENT); |
| break; |
| } |
| |
| std::unique_ptr<dbus::Response> response( |
| dbus::Response::FromMethodCall(method_call)); |
| dbus::MessageWriter writer(response.get()); |
| writer.AppendProtoAsArrayOfBytes(protobuf); |
| std::move(response_sender).Run(std::move(response)); |
| } |
| |
| } // namespace policy |
| } // namespace power_manager |