| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ash/system/chromeos/power/tablet_power_button_controller.h" | |
| 6 | |
| 7 #include "ash/common/accessibility_delegate.h" | |
| 8 #include "ash/common/session/session_state_delegate.h" | |
| 9 #include "ash/common/shell_delegate.h" | |
| 10 #include "ash/common/wm/maximize_mode/maximize_mode_controller.h" | |
| 11 #include "ash/common/wm_shell.h" | |
| 12 #include "ash/shell.h" | |
| 13 #include "ash/wm/lock_state_controller.h" | |
| 14 #include "base/time/default_tick_clock.h" | |
| 15 #include "chromeos/dbus/dbus_thread_manager.h" | |
| 16 #include "ui/events/devices/input_device_manager.h" | |
| 17 #include "ui/events/devices/stylus_state.h" | |
| 18 #include "ui/events/event.h" | |
| 19 | |
| 20 namespace ash { | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 // Amount of time the power button must be held to start the pre-shutdown | |
| 25 // animation when in tablet mode. This differs depending on whether the screen | |
| 26 // is on or off when the power button is initially pressed. | |
| 27 constexpr int kShutdownWhenScreenOnTimeoutMs = 500; | |
| 28 // TODO(derat): This is currently set to a high value to work around delays in | |
| 29 // powerd's reports of button-up events when the preceding button-down event | |
| 30 // turns the display on. Set it to a lower value once powerd no longer blocks on | |
| 31 // asking Chrome to turn the display on: http://crbug.com/685734 | |
| 32 constexpr int kShutdownWhenScreenOffTimeoutMs = 2000; | |
| 33 | |
| 34 // Amount of time since last SuspendDone() that power button event needs to be | |
| 35 // ignored. | |
| 36 constexpr int kIgnorePowerButtonAfterResumeMs = 2000; | |
| 37 | |
| 38 // Ignore button-up events occurring within this many milliseconds of the | |
| 39 // previous button-up event. This prevents us from falling behind if the power | |
| 40 // button is pressed repeatedly. | |
| 41 constexpr int kIgnoreRepeatedButtonUpMs = 500; | |
| 42 | |
| 43 // Returns true if device is a convertible/tablet device, otherwise false. | |
| 44 bool IsTabletModeSupported() { | |
| 45 MaximizeModeController* maximize_mode_controller = | |
| 46 WmShell::Get()->maximize_mode_controller(); | |
| 47 return maximize_mode_controller && | |
| 48 maximize_mode_controller->CanEnterMaximizeMode(); | |
| 49 } | |
| 50 | |
| 51 // Returns true if device is currently in tablet/maximize mode, otherwise false. | |
| 52 bool IsTabletModeActive() { | |
| 53 MaximizeModeController* maximize_mode_controller = | |
| 54 WmShell::Get()->maximize_mode_controller(); | |
| 55 return maximize_mode_controller && | |
| 56 maximize_mode_controller->IsMaximizeModeWindowManagerEnabled(); | |
| 57 } | |
| 58 | |
| 59 } // namespace | |
| 60 | |
| 61 TabletPowerButtonController::TestApi::TestApi( | |
| 62 TabletPowerButtonController* controller) | |
| 63 : controller_(controller) {} | |
| 64 | |
| 65 TabletPowerButtonController::TestApi::~TestApi() {} | |
| 66 | |
| 67 bool TabletPowerButtonController::TestApi::ShutdownTimerIsRunning() const { | |
| 68 return controller_->shutdown_timer_.IsRunning(); | |
| 69 } | |
| 70 | |
| 71 void TabletPowerButtonController::TestApi::TriggerShutdownTimeout() { | |
| 72 DCHECK(ShutdownTimerIsRunning()); | |
| 73 controller_->OnShutdownTimeout(); | |
| 74 controller_->shutdown_timer_.Stop(); | |
| 75 } | |
| 76 | |
| 77 TabletPowerButtonController::TabletPowerButtonController( | |
| 78 LockStateController* controller) | |
| 79 : tick_clock_(new base::DefaultTickClock()), | |
| 80 force_off_on_button_up_(true), | |
| 81 controller_(controller), | |
| 82 weak_ptr_factory_(this) { | |
| 83 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver( | |
| 84 this); | |
| 85 WmShell::Get()->AddShellObserver(this); | |
| 86 // TODO(mash): Provide a way for this class to observe stylus events: | |
| 87 // http://crbug.com/682460 | |
| 88 if (ui::InputDeviceManager::HasInstance()) | |
| 89 ui::InputDeviceManager::GetInstance()->AddObserver(this); | |
| 90 Shell::GetInstance()->PrependPreTargetHandler(this); | |
| 91 | |
| 92 GetInitialBacklightsForcedOff(); | |
| 93 } | |
| 94 | |
| 95 TabletPowerButtonController::~TabletPowerButtonController() { | |
| 96 Shell::GetInstance()->RemovePreTargetHandler(this); | |
| 97 if (ui::InputDeviceManager::HasInstance()) | |
| 98 ui::InputDeviceManager::GetInstance()->RemoveObserver(this); | |
| 99 WmShell::Get()->RemoveShellObserver(this); | |
| 100 chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver( | |
| 101 this); | |
| 102 } | |
| 103 | |
| 104 bool TabletPowerButtonController::ShouldHandlePowerButtonEvents() const { | |
| 105 return IsTabletModeSupported(); | |
| 106 } | |
| 107 | |
| 108 void TabletPowerButtonController::OnPowerButtonEvent( | |
| 109 bool down, | |
| 110 const base::TimeTicks& timestamp) { | |
| 111 if (down) { | |
| 112 force_off_on_button_up_ = true; | |
| 113 // When the system resumes in response to the power button being pressed, | |
| 114 // Chrome receives powerd's SuspendDone signal and notification that the | |
| 115 // backlight has been turned back on before seeing the power button events | |
| 116 // that woke the system. Avoid forcing off display just after resuming to | |
| 117 // ensure that we don't turn the display off in response to the events. | |
| 118 if (timestamp - last_resume_time_ <= | |
| 119 base::TimeDelta::FromMilliseconds(kIgnorePowerButtonAfterResumeMs)) { | |
| 120 force_off_on_button_up_ = false; | |
| 121 } | |
| 122 screen_off_when_power_button_down_ = brightness_level_is_zero_; | |
| 123 SetDisplayForcedOff(false); | |
| 124 StartShutdownTimer(); | |
| 125 } else { | |
| 126 // When power button is released, cancel shutdown animation whenever it is | |
| 127 // still cancellable. | |
| 128 if (controller_->CanCancelShutdownAnimation()) | |
| 129 controller_->CancelShutdownAnimation(); | |
| 130 | |
| 131 const base::TimeTicks previous_up_time = last_button_up_time_; | |
| 132 last_button_up_time_ = tick_clock_->NowTicks(); | |
| 133 // Ignore the event if it comes too soon after the last one. | |
| 134 if (timestamp - previous_up_time <= | |
| 135 base::TimeDelta::FromMilliseconds(kIgnoreRepeatedButtonUpMs)) { | |
| 136 shutdown_timer_.Stop(); | |
| 137 return; | |
| 138 } | |
| 139 | |
| 140 if (shutdown_timer_.IsRunning()) { | |
| 141 shutdown_timer_.Stop(); | |
| 142 if (!screen_off_when_power_button_down_ && force_off_on_button_up_) { | |
| 143 SetDisplayForcedOff(true); | |
| 144 LockScreenIfRequired(); | |
| 145 } | |
| 146 } | |
| 147 } | |
| 148 } | |
| 149 | |
| 150 void TabletPowerButtonController::PowerManagerRestarted() { | |
| 151 chromeos::DBusThreadManager::Get() | |
| 152 ->GetPowerManagerClient() | |
| 153 ->SetBacklightsForcedOff(backlights_forced_off_); | |
| 154 } | |
| 155 | |
| 156 void TabletPowerButtonController::BrightnessChanged(int level, | |
| 157 bool user_initiated) { | |
| 158 brightness_level_is_zero_ = level == 0; | |
| 159 } | |
| 160 | |
| 161 void TabletPowerButtonController::SuspendDone( | |
| 162 const base::TimeDelta& sleep_duration) { | |
| 163 last_resume_time_ = tick_clock_->NowTicks(); | |
| 164 } | |
| 165 | |
| 166 void TabletPowerButtonController::OnMaximizeModeStarted() { | |
| 167 shutdown_timer_.Stop(); | |
| 168 if (controller_->CanCancelShutdownAnimation()) | |
| 169 controller_->CancelShutdownAnimation(); | |
| 170 } | |
| 171 | |
| 172 void TabletPowerButtonController::OnMaximizeModeEnded() { | |
| 173 shutdown_timer_.Stop(); | |
| 174 if (controller_->CanCancelShutdownAnimation()) | |
| 175 controller_->CancelShutdownAnimation(); | |
| 176 } | |
| 177 | |
| 178 void TabletPowerButtonController::OnKeyEvent(ui::KeyEvent* event) { | |
| 179 // Ignore key events generated by the power button since power button activity | |
| 180 // is already handled by OnPowerButtonEvent(). | |
| 181 if (event->key_code() == ui::VKEY_POWER) | |
| 182 return; | |
| 183 | |
| 184 if (!IsTabletModeActive() && backlights_forced_off_) | |
| 185 SetDisplayForcedOff(false); | |
| 186 } | |
| 187 | |
| 188 void TabletPowerButtonController::OnMouseEvent(ui::MouseEvent* event) { | |
| 189 if (event->flags() & ui::EF_IS_SYNTHESIZED) | |
| 190 return; | |
| 191 | |
| 192 if (!IsTabletModeActive() && backlights_forced_off_) | |
| 193 SetDisplayForcedOff(false); | |
| 194 } | |
| 195 | |
| 196 void TabletPowerButtonController::OnStylusStateChanged(ui::StylusState state) { | |
| 197 if (IsTabletModeSupported() && state == ui::StylusState::REMOVED && | |
| 198 backlights_forced_off_) { | |
| 199 SetDisplayForcedOff(false); | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 void TabletPowerButtonController::SetTickClockForTesting( | |
| 204 std::unique_ptr<base::TickClock> tick_clock) { | |
| 205 DCHECK(tick_clock); | |
| 206 tick_clock_ = std::move(tick_clock); | |
| 207 } | |
| 208 | |
| 209 void TabletPowerButtonController::SetDisplayForcedOff(bool forced_off) { | |
| 210 if (backlights_forced_off_ == forced_off) | |
| 211 return; | |
| 212 | |
| 213 // Set the display and keyboard backlights (if present) to |forced_off|. | |
| 214 chromeos::DBusThreadManager::Get() | |
| 215 ->GetPowerManagerClient() | |
| 216 ->SetBacklightsForcedOff(forced_off); | |
| 217 backlights_forced_off_ = forced_off; | |
| 218 | |
| 219 ShellDelegate* delegate = WmShell::Get()->delegate(); | |
| 220 delegate->SetTouchscreenEnabledInPrefs(!forced_off, | |
| 221 true /* use_local_state */); | |
| 222 delegate->UpdateTouchscreenStatusFromPrefs(); | |
| 223 | |
| 224 // Send an a11y alert. | |
| 225 WmShell::Get()->accessibility_delegate()->TriggerAccessibilityAlert( | |
| 226 forced_off ? A11Y_ALERT_SCREEN_OFF : A11Y_ALERT_SCREEN_ON); | |
| 227 } | |
| 228 | |
| 229 void TabletPowerButtonController::GetInitialBacklightsForcedOff() { | |
| 230 chromeos::DBusThreadManager::Get() | |
| 231 ->GetPowerManagerClient() | |
| 232 ->GetBacklightsForcedOff(base::Bind( | |
| 233 &TabletPowerButtonController::OnGotInitialBacklightsForcedOff, | |
| 234 weak_ptr_factory_.GetWeakPtr())); | |
| 235 } | |
| 236 | |
| 237 void TabletPowerButtonController::OnGotInitialBacklightsForcedOff( | |
| 238 bool is_forced_off) { | |
| 239 backlights_forced_off_ = is_forced_off; | |
| 240 } | |
| 241 | |
| 242 void TabletPowerButtonController::StartShutdownTimer() { | |
| 243 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( | |
| 244 screen_off_when_power_button_down_ ? kShutdownWhenScreenOffTimeoutMs | |
| 245 : kShutdownWhenScreenOnTimeoutMs); | |
| 246 shutdown_timer_.Start(FROM_HERE, timeout, this, | |
| 247 &TabletPowerButtonController::OnShutdownTimeout); | |
| 248 } | |
| 249 | |
| 250 void TabletPowerButtonController::OnShutdownTimeout() { | |
| 251 controller_->StartShutdownAnimation(); | |
| 252 } | |
| 253 | |
| 254 void TabletPowerButtonController::LockScreenIfRequired() { | |
| 255 SessionStateDelegate* session_state_delegate = | |
| 256 WmShell::Get()->GetSessionStateDelegate(); | |
| 257 if (session_state_delegate->ShouldLockScreenAutomatically() && | |
| 258 session_state_delegate->CanLockScreen() && | |
| 259 !session_state_delegate->IsUserSessionBlocked() && | |
| 260 !controller_->LockRequested()) { | |
| 261 session_state_delegate->LockScreen(); | |
| 262 } | |
| 263 } | |
| 264 | |
| 265 } // namespace ash | |
| OLD | NEW |