power_button_controller.cc 22 KB

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