1 // Copyright (c) 2012 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/power/power_button_controller.h"
6
7 #include <limits>
8 #include <memory>
9 #include <string>
10 #include <utility>
11
12 #include "ash/accelerators/accelerator_controller_impl.h"
13 #include "ash/public/cpp/ash_switches.h"
14 #include "ash/public/cpp/shell_window_ids.h"
15 #include "ash/session/session_controller_impl.h"
16 #include "ash/shell.h"
17 #include "ash/shutdown_reason.h"
18 #include "ash/system/power/power_button_display_controller.h"
19 #include "ash/system/power/power_button_menu_item_view.h"
20 #include "ash/system/power/power_button_menu_metrics_type.h"
21 #include "ash/system/power/power_button_menu_screen_view.h"
22 #include "ash/system/power/power_button_menu_view.h"
23 #include "ash/system/power/power_button_screenshot_controller.h"
24 #include "ash/wm/lock_state_controller.h"
25 #include "ash/wm/session_state_animator.h"
26 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
27 #include "ash/wm/window_util.h"
28 #include "base/bind.h"
29 #include "base/command_line.h"
30 #include "base/json/json_reader.h"
31 #include "base/time/default_tick_clock.h"
32 #include "chromeos/dbus/power_manager/backlight.pb.h"
33 #include "ui/display/types/display_snapshot.h"
34 #include "ui/views/widget/widget.h"
35 #include "ui/views/widget/widget_observer.h"
36
37 namespace ash {
38 namespace {
39
40 // Amount of time power button must be held to start the power menu animation
41 // for convertible/slate/detachable devices. This differs depending on whether
42 // the screen is on or off when the power button is initially pressed.
43 constexpr base::TimeDelta kShowMenuWhenScreenOnTimeout =
44 base::TimeDelta::FromMilliseconds(500);
45 constexpr base::TimeDelta kShowMenuWhenScreenOffTimeout =
46 base::TimeDelta::FromMilliseconds(2000);
47
48 // Time that power button should be pressed after power menu is shown before
49 // starting the cancellable pre-shutdown animation.
50 constexpr base::TimeDelta kStartShutdownAnimationTimeout =
51 base::TimeDelta::FromMilliseconds(650);
52
53 enum PowerButtonUpState {
54 UP_NONE = 0,
55 UP_MENU_TIMER_WAS_RUNNING = 1 << 0,
56 UP_PRE_SHUTDOWN_TIMER_WAS_RUNNING = 1 << 1,
57 UP_SHOWING_ANIMATION_CANCELLED = 1 << 2,
58 UP_CAN_CANCEL_SHUTDOWN_ANIMATION = 1 << 3,
59 UP_MENU_WAS_OPENED = 1 << 4,
60 };
61
62 // Creates a fullscreen widget responsible for showing the power button menu.
CreateMenuWidget()63 std::unique_ptr<views::Widget> CreateMenuWidget() {
64 auto menu_widget = std::make_unique<views::Widget>();
65 views::Widget::InitParams params(
66 views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
67 params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
68 params.z_order = ui::ZOrderLevel::kFloatingWindow;
69 params.accept_events = true;
70 params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
71 params.name = "PowerButtonMenuWindow";
72 params.layer_type = ui::LAYER_SOLID_COLOR;
73 params.parent = Shell::GetPrimaryRootWindow()->GetChildById(
74 kShellWindowId_PowerMenuContainer);
75 menu_widget->Init(std::move(params));
76
77 gfx::Rect widget_bounds =
78 display::Screen::GetScreen()->GetPrimaryDisplay().bounds();
79 menu_widget->SetBounds(widget_bounds);
80
81 // Enable arrow key - arrow right/left and down/up triggers the same focus
82 // movement as tab/shift+tab.
83 menu_widget->widget_delegate()->SetEnableArrowKeyTraversal(true);
84 return menu_widget;
85 }
86
87 } // namespace
88
89 constexpr base::TimeDelta PowerButtonController::kScreenStateChangeDelay;
90
91 constexpr base::TimeDelta PowerButtonController::kIgnoreRepeatedButtonUpDelay;
92
93 constexpr base::TimeDelta
94 PowerButtonController::kIgnorePowerButtonAfterResumeDelay;
95
96 constexpr const char* PowerButtonController::kEdgeField;
97 constexpr const char* PowerButtonController::kLeftEdge;
98 constexpr const char* PowerButtonController::kRightEdge;
99 constexpr const char* PowerButtonController::kTopEdge;
100 constexpr const char* PowerButtonController::kBottomEdge;
101
PowerButtonController(BacklightsForcedOffSetter * backlights_forced_off_setter)102 PowerButtonController::PowerButtonController(
103 BacklightsForcedOffSetter* backlights_forced_off_setter)
104 : backlights_forced_off_setter_(backlights_forced_off_setter),
105 lock_state_controller_(Shell::Get()->lock_state_controller()),
106 tick_clock_(base::DefaultTickClock::GetInstance()),
107 backlights_forced_off_observer_(this) {
108 ProcessCommandLine();
109 display_controller_ = std::make_unique<PowerButtonDisplayController>(
110 backlights_forced_off_setter_, tick_clock_);
111 chromeos::PowerManagerClient* power_manager_client =
112 chromeos::PowerManagerClient::Get();
113 power_manager_client->AddObserver(this);
114 power_manager_client->GetSwitchStates(base::BindOnce(
115 &PowerButtonController::OnGetSwitchStates, weak_factory_.GetWeakPtr()));
116 AccelerometerReader::GetInstance()->AddObserver(this);
117 auto* shell = Shell::Get();
118 shell->display_configurator()->AddObserver(this);
119 backlights_forced_off_observer_.Add(backlights_forced_off_setter);
120 shell->tablet_mode_controller()->AddObserver(this);
121 shell->lock_state_controller()->AddObserver(this);
122 shell->session_controller()->AddObserver(this);
123 }
124
~PowerButtonController()125 PowerButtonController::~PowerButtonController() {
126 auto* shell = Shell::Get();
127 shell->session_controller()->RemoveObserver(this);
128 shell->lock_state_controller()->RemoveObserver(this);
129 if (shell->tablet_mode_controller())
130 shell->tablet_mode_controller()->RemoveObserver(this);
131 shell->display_configurator()->RemoveObserver(this);
132 AccelerometerReader::GetInstance()->RemoveObserver(this);
133 chromeos::PowerManagerClient::Get()->RemoveObserver(this);
134 }
135
OnPreShutdownTimeout()136 void PowerButtonController::OnPreShutdownTimeout() {
137 lock_state_controller_->StartShutdownAnimation(ShutdownReason::POWER_BUTTON);
138 DCHECK(menu_widget_);
139 static_cast<PowerButtonMenuScreenView*>(menu_widget_->GetContentsView())
140 ->power_button_menu_view()
141 ->FocusPowerOffButton();
142 }
143
OnLegacyPowerButtonEvent(bool down)144 void PowerButtonController::OnLegacyPowerButtonEvent(bool down) {
145 // Avoid starting the lock/shutdown sequence if the power button is pressed
146 // while the screen is off (http://crbug.com/128451), unless an external
147 // display is still on (http://crosbug.com/p/24912).
148 if (brightness_is_zero_ && !internal_display_off_and_external_display_on_)
149 return;
150
151 if (!down)
152 return;
153
154 // Ignore the power button down event if the menu is partially opened.
155 if (IsMenuOpened() && !show_menu_animation_done_)
156 return;
157
158 // If power button releases won't get reported correctly because we're not
159 // running on official hardware, show menu animation on the first power
160 // button press. On a further press while the menu is open, simply shut down
161 // (http://crbug.com/945005).
162 if (!show_menu_animation_done_)
163 StartPowerMenuAnimation();
164 else
165 lock_state_controller_->RequestShutdown(ShutdownReason::POWER_BUTTON);
166 }
167
OnPowerButtonEvent(bool down,const base::TimeTicks & timestamp)168 void PowerButtonController::OnPowerButtonEvent(
169 bool down,
170 const base::TimeTicks& timestamp) {
171 if (down) {
172 force_off_on_button_up_ = false;
173 if (UseTabletBehavior()) {
174 force_off_on_button_up_ = true;
175
176 // When the system resumes in response to the power button being pressed,
177 // Chrome receives powerd's SuspendDone signal and notification that the
178 // backlight has been turned back on before seeing the power button events
179 // that woke the system. Avoid forcing off display just after resuming to
180 // ensure that we don't turn the display off in response to the events.
181 if (timestamp - last_resume_time_ <= kIgnorePowerButtonAfterResumeDelay)
182 force_off_on_button_up_ = false;
183
184 // The actual display may remain off for a short period after powerd asks
185 // Chrome to turn it on. If the user presses the power button again during
186 // this time, they probably intend to turn the display on. Avoid forcing
187 // off in this case.
188 if (timestamp - display_controller_->screen_state_last_changed() <=
189 kScreenStateChangeDelay) {
190 force_off_on_button_up_ = false;
191 }
192 }
193
194 screen_off_when_power_button_down_ = !display_controller_->IsScreenOn();
195 menu_shown_when_power_button_down_ = show_menu_animation_done_;
196 display_controller_->SetBacklightsForcedOff(false);
197
198 if (menu_shown_when_power_button_down_) {
199 pre_shutdown_timer_.Start(FROM_HERE, kStartShutdownAnimationTimeout, this,
200 &PowerButtonController::OnPreShutdownTimeout);
201 return;
202 }
203
204 if (!UseTabletBehavior()) {
205 StartPowerMenuAnimation();
206 } else {
207 base::TimeDelta timeout = screen_off_when_power_button_down_
208 ? kShowMenuWhenScreenOffTimeout
209 : kShowMenuWhenScreenOnTimeout;
210
211 power_button_menu_timer_.Start(
212 FROM_HERE, timeout, this,
213 &PowerButtonController::StartPowerMenuAnimation);
214 }
215 } else {
216 uint32_t up_state = UP_NONE;
217 if (lock_state_controller_->CanCancelShutdownAnimation()) {
218 up_state |= UP_CAN_CANCEL_SHUTDOWN_ANIMATION;
219 lock_state_controller_->CancelShutdownAnimation();
220 }
221 const base::TimeTicks previous_up_time = last_button_up_time_;
222 last_button_up_time_ = timestamp;
223
224 const bool menu_timer_was_running = power_button_menu_timer_.IsRunning();
225 const bool pre_shutdown_timer_was_running = pre_shutdown_timer_.IsRunning();
226 power_button_menu_timer_.Stop();
227 pre_shutdown_timer_.Stop();
228
229 const bool menu_was_partially_opened =
230 IsMenuOpened() && !show_menu_animation_done_;
231 // Cancel the menu animation if it's still ongoing when the button is
232 // released.
233 if (menu_was_partially_opened) {
234 static_cast<PowerButtonMenuScreenView*>(menu_widget_->GetContentsView())
235 ->ScheduleShowHideAnimation(false);
236 up_state |= UP_SHOWING_ANIMATION_CANCELLED;
237 }
238
239 // If the button is tapped (i.e. not held long enough to start the
240 // cancellable shutdown animation) while the menu is open, dismiss the menu.
241 if (menu_shown_when_power_button_down_ && pre_shutdown_timer_was_running)
242 DismissMenu();
243
244 // Ignore the event if it comes too soon after the last one.
245 if (timestamp - previous_up_time <= kIgnoreRepeatedButtonUpDelay)
246 return;
247
248 if (!screen_off_when_power_button_down_) {
249 if (menu_timer_was_running)
250 up_state |= UP_MENU_TIMER_WAS_RUNNING;
251 if (pre_shutdown_timer_was_running)
252 up_state |= UP_PRE_SHUTDOWN_TIMER_WAS_RUNNING;
253 if (show_menu_animation_done_)
254 up_state |= UP_MENU_WAS_OPENED;
255 UpdatePowerButtonEventUMAHistogram(up_state);
256 }
257
258 if (screen_off_when_power_button_down_ || !force_off_on_button_up_)
259 return;
260
261 if (menu_timer_was_running || menu_was_partially_opened ||
262 (menu_shown_when_power_button_down_ &&
263 pre_shutdown_timer_was_running)) {
264 display_controller_->SetBacklightsForcedOff(true);
265 LockScreenIfRequired();
266 }
267 }
268 }
269
OnLockButtonEvent(bool down,const base::TimeTicks & timestamp)270 void PowerButtonController::OnLockButtonEvent(
271 bool down,
272 const base::TimeTicks& timestamp) {
273 lock_button_down_ = down;
274
275 // Ignore the lock button behavior if power button is being pressed.
276 if (power_button_down_)
277 return;
278
279 const SessionControllerImpl* const session_controller =
280 Shell::Get()->session_controller();
281 if (!session_controller->CanLockScreen() ||
282 session_controller->IsScreenLocked() ||
283 lock_state_controller_->LockRequested() ||
284 lock_state_controller_->ShutdownRequested()) {
285 return;
286 }
287
288 if (down)
289 lock_state_controller_->StartLockAnimation();
290 else
291 lock_state_controller_->CancelLockAnimation();
292 }
293
CancelPowerButtonEvent()294 void PowerButtonController::CancelPowerButtonEvent() {
295 force_off_on_button_up_ = false;
296 StopTimersAndDismissMenu();
297 }
298
IsMenuOpened() const299 bool PowerButtonController::IsMenuOpened() const {
300 return menu_widget_ && menu_widget_->GetLayer()->GetTargetVisibility();
301 }
302
DismissMenu()303 void PowerButtonController::DismissMenu() {
304 if (IsMenuOpened()) {
305 static_cast<PowerButtonMenuScreenView*>(menu_widget_->GetContentsView())
306 ->ResetOpacity();
307 menu_widget_->Hide();
308 }
309
310 show_menu_animation_done_ = false;
311 active_window_paint_as_active_lock_.reset();
312 }
313
StopForcingBacklightsOff()314 void PowerButtonController::StopForcingBacklightsOff() {
315 display_controller_->SetBacklightsForcedOff(false);
316 }
317
OnDisplayModeChanged(const display::DisplayConfigurator::DisplayStateList & display_states)318 void PowerButtonController::OnDisplayModeChanged(
319 const display::DisplayConfigurator::DisplayStateList& display_states) {
320 bool internal_display_off = false;
321 bool external_display_on = false;
322 for (const display::DisplaySnapshot* display : display_states) {
323 if (display->type() == display::DISPLAY_CONNECTION_TYPE_INTERNAL) {
324 if (!display->current_mode())
325 internal_display_off = true;
326 } else if (display->current_mode()) {
327 external_display_on = true;
328 }
329 }
330 internal_display_off_and_external_display_on_ =
331 internal_display_off && external_display_on;
332 }
333
ScreenBrightnessChanged(const power_manager::BacklightBrightnessChange & change)334 void PowerButtonController::ScreenBrightnessChanged(
335 const power_manager::BacklightBrightnessChange& change) {
336 brightness_is_zero_ =
337 change.percent() <= std::numeric_limits<double>::epsilon();
338 }
339
PowerButtonEventReceived(bool down,const base::TimeTicks & timestamp)340 void PowerButtonController::PowerButtonEventReceived(
341 bool down,
342 const base::TimeTicks& timestamp) {
343 if (lock_state_controller_->ShutdownRequested())
344 return;
345
346 // Handle tablet mode power button screenshot accelerator.
347 if (screenshot_controller_ &&
348 screenshot_controller_->OnPowerButtonEvent(down, timestamp)) {
349 return;
350 }
351
352 power_button_down_ = down;
353 // Ignore power button if lock button is being pressed.
354 if (lock_button_down_)
355 return;
356
357 button_type_ == ButtonType::LEGACY ? OnLegacyPowerButtonEvent(down)
358 : OnPowerButtonEvent(down, timestamp);
359 }
360
SuspendImminent(power_manager::SuspendImminent::Reason reason)361 void PowerButtonController::SuspendImminent(
362 power_manager::SuspendImminent::Reason reason) {
363 DismissMenu();
364 }
365
SuspendDone(const base::TimeDelta & sleep_duration)366 void PowerButtonController::SuspendDone(const base::TimeDelta& sleep_duration) {
367 last_resume_time_ = tick_clock_->NowTicks();
368 }
369
OnLoginStatusChanged(LoginStatus status)370 void PowerButtonController::OnLoginStatusChanged(LoginStatus status) {
371 // Destroy |menu_widget_| on login status change to reset the content of the
372 // menu since the menu items change if login stauts changed.
373 menu_widget_.reset();
374 }
375
OnGetSwitchStates(base::Optional<chromeos::PowerManagerClient::SwitchStates> result)376 void PowerButtonController::OnGetSwitchStates(
377 base::Optional<chromeos::PowerManagerClient::SwitchStates> result) {
378 if (!result.has_value())
379 return;
380
381 if (result->tablet_mode !=
382 chromeos::PowerManagerClient::TabletMode::UNSUPPORTED) {
383 has_tablet_mode_switch_ = true;
384 InitTabletPowerButtonMembers();
385 }
386 }
387
OnAccelerometerUpdated(scoped_refptr<const AccelerometerUpdate> update)388 void PowerButtonController::OnAccelerometerUpdated(
389 scoped_refptr<const AccelerometerUpdate> update) {
390 if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::UNKNOWN) {
391 ec_lid_angle_driver_status_ =
392 AccelerometerReader::GetInstance()->GetECLidAngleDriverStatus();
393 }
394
395 // When ChromeOS EC lid angle driver is supported, there's always tablet mode
396 // switch in device, so PowerButtonController doesn't need to listens to
397 // accelerometer events.
398 if (ec_lid_angle_driver_status_ == ECLidAngleDriverStatus::SUPPORTED) {
399 AccelerometerReader::GetInstance()->RemoveObserver(this);
400 return;
401 }
402
403 if (!has_tablet_mode_switch_ && observe_accelerometer_events_)
404 InitTabletPowerButtonMembers();
405 }
406
OnBacklightsForcedOffChanged(bool forced_off)407 void PowerButtonController::OnBacklightsForcedOffChanged(bool forced_off) {
408 DismissMenu();
409 }
410
OnScreenStateChanged(ScreenState screen_state)411 void PowerButtonController::OnScreenStateChanged(ScreenState screen_state) {
412 if (screen_state != ScreenState::ON)
413 DismissMenu();
414 }
415
OnTabletModeStarted()416 void PowerButtonController::OnTabletModeStarted() {
417 in_tablet_mode_ = true;
418 StopTimersAndDismissMenu();
419 }
420
OnTabletModeEnded()421 void PowerButtonController::OnTabletModeEnded() {
422 in_tablet_mode_ = false;
423 StopTimersAndDismissMenu();
424 }
425
OnLockStateEvent(LockStateObserver::EventType event)426 void PowerButtonController::OnLockStateEvent(
427 LockStateObserver::EventType event) {
428 // Reset |lock_button_down_| when lock animation finished. LOCK_RELEASED is
429 // not allowed when screen is locked, which means OnLockButtonEvent will not
430 // be called in lock screen. This will lead |lock_button_down_| to stay in a
431 // dirty state if press lock button after login but release in lock screen.
432 if (event == EVENT_LOCK_ANIMATION_FINISHED)
433 lock_button_down_ = false;
434 }
435
UseTabletBehavior() const436 bool PowerButtonController::UseTabletBehavior() const {
437 return in_tablet_mode_ || force_tablet_power_button_;
438 }
439
StopTimersAndDismissMenu()440 void PowerButtonController::StopTimersAndDismissMenu() {
441 pre_shutdown_timer_.Stop();
442 power_button_menu_timer_.Stop();
443 DismissMenu();
444 }
445
StartPowerMenuAnimation()446 void PowerButtonController::StartPowerMenuAnimation() {
447 // Avoid a distracting deactivation animation on the formerly-active
448 // window when the menu is activated.
449 views::Widget* active_toplevel_widget =
450 views::Widget::GetTopLevelWidgetForNativeView(
451 window_util::GetActiveWindow());
452 active_window_paint_as_active_lock_ =
453 active_toplevel_widget ? active_toplevel_widget->LockPaintAsActive()
454 : nullptr;
455
456 if (!menu_widget_) {
457 menu_widget_ = CreateMenuWidget();
458 menu_widget_->SetContentsView(std::make_unique<PowerButtonMenuScreenView>(
459 power_button_position_, power_button_offset_percentage_,
460 base::BindRepeating(&PowerButtonController::SetShowMenuAnimationDone,
461 base::Unretained(this))));
462 }
463 auto* contents_view =
464 static_cast<PowerButtonMenuScreenView*>(menu_widget_->GetContentsView());
465 contents_view->OnWidgetShown(power_button_position_,
466 power_button_offset_percentage_);
467 menu_widget_->Show();
468
469 // Hide cursor, but let it reappear if the mouse moves.
470 Shell* shell = Shell::Get();
471 if (shell->cursor_manager())
472 shell->cursor_manager()->HideCursor();
473
474 contents_view->ScheduleShowHideAnimation(true);
475 }
476
ProcessCommandLine()477 void PowerButtonController::ProcessCommandLine() {
478 const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
479 button_type_ = cl->HasSwitch(switches::kAuraLegacyPowerButton)
480 ? ButtonType::LEGACY
481 : ButtonType::NORMAL;
482 observe_accelerometer_events_ = cl->HasSwitch(switches::kAshEnableTabletMode);
483 force_tablet_power_button_ = cl->HasSwitch(switches::kForceTabletPowerButton);
484
485 ParsePowerButtonPositionSwitch();
486 }
487
InitTabletPowerButtonMembers()488 void PowerButtonController::InitTabletPowerButtonMembers() {
489 if (!screenshot_controller_) {
490 screenshot_controller_ =
491 std::make_unique<PowerButtonScreenshotController>(tick_clock_);
492 }
493 }
494
LockScreenIfRequired()495 void PowerButtonController::LockScreenIfRequired() {
496 const SessionControllerImpl* session_controller =
497 Shell::Get()->session_controller();
498 if (session_controller->ShouldLockScreenAutomatically() &&
499 session_controller->CanLockScreen() &&
500 !session_controller->IsUserSessionBlocked() &&
501 !lock_state_controller_->LockRequested()) {
502 lock_state_controller_->LockWithoutAnimation();
503 }
504 }
505
SetShowMenuAnimationDone()506 void PowerButtonController::SetShowMenuAnimationDone() {
507 show_menu_animation_done_ = true;
508 if (button_type_ != ButtonType::LEGACY) {
509 pre_shutdown_timer_.Start(FROM_HERE, kStartShutdownAnimationTimeout, this,
510 &PowerButtonController::OnPreShutdownTimeout);
511 }
512 }
513
ParsePowerButtonPositionSwitch()514 void PowerButtonController::ParsePowerButtonPositionSwitch() {
515 const base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
516 if (!cl->HasSwitch(switches::kAshPowerButtonPosition))
517 return;
518
519 std::unique_ptr<base::DictionaryValue> position_info =
520 base::DictionaryValue::From(base::JSONReader::ReadDeprecated(
521 cl->GetSwitchValueASCII(switches::kAshPowerButtonPosition)));
522 if (!position_info) {
523 LOG(ERROR) << switches::kAshPowerButtonPosition << " flag has no value";
524 return;
525 }
526
527 std::string edge, position;
528 if (!position_info->GetString(kEdgeField, &edge) ||
529 !position_info->GetDouble(kPositionField,
530 &power_button_offset_percentage_)) {
531 LOG(ERROR) << "Both " << kEdgeField << " field and " << kPositionField
532 << " are always needed if " << switches::kAshPowerButtonPosition
533 << " is set";
534 return;
535 }
536
537 if (edge == kLeftEdge) {
538 power_button_position_ = PowerButtonPosition::LEFT;
539 } else if (edge == kRightEdge) {
540 power_button_position_ = PowerButtonPosition::RIGHT;
541 } else if (edge == kTopEdge) {
542 power_button_position_ = PowerButtonPosition::TOP;
543 } else if (edge == kBottomEdge) {
544 power_button_position_ = PowerButtonPosition::BOTTOM;
545 } else {
546 LOG(ERROR) << "Invalid " << kEdgeField << " field in "
547 << switches::kAshPowerButtonPosition;
548 return;
549 }
550
551 if (power_button_offset_percentage_ < 0 ||
552 power_button_offset_percentage_ > 1.0f) {
553 LOG(ERROR) << "Invalid " << kPositionField << " field in "
554 << switches::kAshPowerButtonPosition;
555 power_button_position_ = PowerButtonPosition::NONE;
556 }
557 }
558
UpdatePowerButtonEventUMAHistogram(uint32_t up_state)559 void PowerButtonController::UpdatePowerButtonEventUMAHistogram(
560 uint32_t up_state) {
561 if (up_state & UP_SHOWING_ANIMATION_CANCELLED)
562 RecordPressInLaptopModeHistogram(PowerButtonPressType::kTapWithoutMenu);
563
564 if (up_state & UP_MENU_TIMER_WAS_RUNNING)
565 RecordPressInTabletModeHistogram(PowerButtonPressType::kTapWithoutMenu);
566
567 if (menu_shown_when_power_button_down_) {
568 if (up_state & UP_PRE_SHUTDOWN_TIMER_WAS_RUNNING) {
569 force_off_on_button_up_
570 ? RecordPressInTabletModeHistogram(PowerButtonPressType::kTapWithMenu)
571 : RecordPressInLaptopModeHistogram(
572 PowerButtonPressType::kTapWithMenu);
573 } else if (!(up_state & UP_CAN_CANCEL_SHUTDOWN_ANIMATION)) {
574 force_off_on_button_up_
575 ? RecordPressInTabletModeHistogram(
576 PowerButtonPressType::kLongPressWithMenuToShutdown)
577 : RecordPressInLaptopModeHistogram(
578 PowerButtonPressType::kLongPressWithMenuToShutdown);
579 }
580 } else if (up_state & UP_MENU_WAS_OPENED) {
581 if (up_state & UP_PRE_SHUTDOWN_TIMER_WAS_RUNNING ||
582 up_state & UP_CAN_CANCEL_SHUTDOWN_ANIMATION) {
583 force_off_on_button_up_ ? RecordPressInTabletModeHistogram(
584 PowerButtonPressType::kLongPressToShowMenu)
585 : RecordPressInLaptopModeHistogram(
586 PowerButtonPressType::kLongPressToShowMenu);
587 } else if (!(up_state & UP_SHOWING_ANIMATION_CANCELLED)) {
588 force_off_on_button_up_
589 ? RecordPressInTabletModeHistogram(
590 PowerButtonPressType::kLongPressWithoutMenuToShutdown)
591 : RecordPressInLaptopModeHistogram(
592 PowerButtonPressType::kLongPressWithoutMenuToShutdown);
593 }
594 }
595 }
596
597 } // namespace ash
598