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