1 // Copyright 2013 The Chromium Authors
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 "base/power_monitor/power_monitor_device_source.h"
6
7 #include <string>
8
9 #include "base/logging.h"
10 #include "base/power_monitor/power_monitor.h"
11 #include "base/power_monitor/power_monitor_source.h"
12 #include "base/strings/string_util.h"
13 #include "base/task/current_thread.h"
14 #include "base/task/sequenced_task_runner.h"
15 #include "base/task/thread_pool.h"
16 #include "base/win/wrapped_window_proc.h"
17
18 namespace base {
19
ProcessPowerEventHelper(PowerMonitorSource::PowerEvent event)20 void ProcessPowerEventHelper(PowerMonitorSource::PowerEvent event) {
21 PowerMonitorSource::ProcessPowerEvent(event);
22 }
23
24 namespace {
25
26 constexpr wchar_t kWindowClassName[] = L"Base_PowerMessageWindow";
27
ProcessWmPowerBroadcastMessage(WPARAM event_id)28 void ProcessWmPowerBroadcastMessage(WPARAM event_id) {
29 PowerMonitorSource::PowerEvent power_event;
30 switch (event_id) {
31 case PBT_APMPOWERSTATUSCHANGE: // The power status changed.
32 power_event = PowerMonitorSource::POWER_STATE_EVENT;
33 break;
34 case PBT_APMRESUMEAUTOMATIC: // Resume from suspend.
35 // We don't notify for PBT_APMRESUMESUSPEND
36 // because, if it occurs, it is always sent as a
37 // second event after PBT_APMRESUMEAUTOMATIC.
38 power_event = PowerMonitorSource::RESUME_EVENT;
39 break;
40 case PBT_APMSUSPEND: // System has been suspended.
41 power_event = PowerMonitorSource::SUSPEND_EVENT;
42 break;
43 default:
44 return;
45
46 // Other Power Events:
47 // PBT_APMBATTERYLOW - removed in Vista.
48 // PBT_APMOEMEVENT - removed in Vista.
49 // PBT_APMQUERYSUSPEND - removed in Vista.
50 // PBT_APMQUERYSUSPENDFAILED - removed in Vista.
51 // PBT_APMRESUMECRITICAL - removed in Vista.
52 // PBT_POWERSETTINGCHANGE - user changed the power settings.
53 }
54
55 ProcessPowerEventHelper(power_event);
56 }
57
58 } // namespace
59
PlatformInit()60 void PowerMonitorDeviceSource::PlatformInit() {
61 // Only for testing.
62 if (!CurrentUIThread::IsSet()) {
63 return;
64 }
65 speed_limit_observer_ =
66 std::make_unique<base::SequenceBound<SpeedLimitObserverWin>>(
67 base::ThreadPool::CreateSequencedTaskRunner({}),
68 BindRepeating(&PowerMonitorSource::ProcessSpeedLimitEvent));
69 }
70
PlatformDestroy()71 void PowerMonitorDeviceSource::PlatformDestroy() {
72 // Because |speed_limit_observer_| is sequence bound, the actual destruction
73 // happens asynchronously on its task runner. Until this has completed it is
74 // still possible for PowerMonitorSource::ProcessSpeedLimitEvent to be called.
75 speed_limit_observer_.reset();
76 }
77
78 // Function to query the system to see if it is currently running on
79 // battery power. Returns true if running on battery.
IsOnBatteryPower()80 bool PowerMonitorDeviceSource::IsOnBatteryPower() {
81 SYSTEM_POWER_STATUS status;
82 if (!::GetSystemPowerStatus(&status)) {
83 DPLOG(ERROR) << "GetSystemPowerStatus failed";
84 return false;
85 }
86 return (status.ACLineStatus == 0);
87 }
88
GetInitialSpeedLimit()89 int PowerMonitorDeviceSource::GetInitialSpeedLimit() {
90 // Returns the maximum value once at start. Subsequent actual values will be
91 // provided asynchronously via callbacks instead.
92 return PowerThermalObserver::kSpeedLimitMax;
93 }
94
PowerMessageWindow()95 PowerMonitorDeviceSource::PowerMessageWindow::PowerMessageWindow() {
96 if (!CurrentUIThread::IsSet()) {
97 // Creating this window in (e.g.) a renderer inhibits shutdown on Windows.
98 // See http://crbug.com/230122. TODO(vandebo): http://crbug.com/236031
99 DLOG(ERROR)
100 << "Cannot create windows on non-UI thread, power monitor disabled!";
101 return;
102 }
103 WNDCLASSEX window_class;
104 base::win::InitializeWindowClass(
105 kWindowClassName,
106 &base::win::WrappedWindowProc<
107 PowerMonitorDeviceSource::PowerMessageWindow::WndProcThunk>,
108 0, 0, 0, nullptr, nullptr, nullptr, nullptr, nullptr, &window_class);
109 instance_ = window_class.hInstance;
110 ATOM clazz = ::RegisterClassEx(&window_class);
111 DCHECK(clazz);
112
113 message_hwnd_ =
114 ::CreateWindowEx(WS_EX_NOACTIVATE, kWindowClassName, nullptr, WS_POPUP, 0,
115 0, 0, 0, nullptr, nullptr, instance_, nullptr);
116 if (message_hwnd_) {
117 // On machines with modern standby calling RegisterSuspendResumeNotification
118 // is required in order to get the PBT_APMSUSPEND message.
119 power_notify_handle_ = ::RegisterSuspendResumeNotification(
120 message_hwnd_, DEVICE_NOTIFY_WINDOW_HANDLE);
121 }
122 }
123
~PowerMessageWindow()124 PowerMonitorDeviceSource::PowerMessageWindow::~PowerMessageWindow() {
125 if (message_hwnd_) {
126 if (power_notify_handle_)
127 ::UnregisterSuspendResumeNotification(power_notify_handle_);
128
129 ::DestroyWindow(message_hwnd_);
130 ::UnregisterClass(kWindowClassName, instance_);
131 }
132 }
133
134 // static
WndProcThunk(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam)135 LRESULT CALLBACK PowerMonitorDeviceSource::PowerMessageWindow::WndProcThunk(
136 HWND hwnd,
137 UINT message,
138 WPARAM wparam,
139 LPARAM lparam) {
140 switch (message) {
141 case WM_POWERBROADCAST:
142 ProcessWmPowerBroadcastMessage(wparam);
143 return TRUE;
144 default:
145 return ::DefWindowProc(hwnd, message, wparam, lparam);
146 }
147 }
148
149 } // namespace base
150