OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 "device/battery/battery_status_manager_win.h" | |
6 | |
7 #include <memory> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/macros.h" | |
11 #include "base/metrics/histogram_macros.h" | |
12 #include "base/strings/string16.h" | |
13 #include "base/win/message_window.h" | |
14 #include "base/win/windows_version.h" | |
15 #include "device/battery/battery_status_manager.h" | |
16 | |
17 namespace device { | |
18 | |
19 namespace { | |
20 | |
21 typedef BatteryStatusService::BatteryUpdateCallback BatteryCallback; | |
22 | |
23 const wchar_t kWindowClassName[] = L"BatteryStatusMessageWindow"; | |
24 | |
25 // This enum is used for histogram. Don't change the order of the existing | |
26 // values. | |
27 enum NumberBatteriesType { | |
28 UNKNOWN_BATTERIES = 0, | |
29 NO_BATTERY = 1, | |
30 ONE_OR_MORE_BATTERIES = 2, | |
31 BATTERY_TYPES_COUNT = 3, | |
32 }; | |
33 | |
34 void UpdateNumberBatteriesHistogram(NumberBatteriesType count) { | |
35 UMA_HISTOGRAM_ENUMERATION("BatteryStatus.NumberBatteriesWin", | |
36 count, | |
37 BATTERY_TYPES_COUNT); | |
38 } | |
39 | |
40 void UpdateNumberBatteriesHistogram() { | |
41 SYSTEM_POWER_STATUS win_status; | |
42 if (!GetSystemPowerStatus(&win_status)) { | |
43 UpdateNumberBatteriesHistogram(UNKNOWN_BATTERIES); | |
44 return; | |
45 } | |
46 | |
47 if (win_status.BatteryFlag == 255) | |
48 UpdateNumberBatteriesHistogram(UNKNOWN_BATTERIES); | |
49 else if (win_status.BatteryFlag == 128) | |
50 UpdateNumberBatteriesHistogram(NO_BATTERY); | |
51 else | |
52 UpdateNumberBatteriesHistogram(ONE_OR_MORE_BATTERIES); | |
53 } | |
54 | |
55 // Message-only window for handling battery changes on Windows. | |
56 class BatteryStatusObserver { | |
57 public: | |
58 explicit BatteryStatusObserver(const BatteryCallback& callback) | |
59 : power_handle_(NULL), | |
60 battery_change_handle_(NULL), | |
61 callback_(callback) { | |
62 } | |
63 | |
64 ~BatteryStatusObserver() { DCHECK(!window_); } | |
65 | |
66 void Start() { | |
67 if (CreateMessageWindow()) { | |
68 BatteryChanged(); | |
69 // RegisterPowerSettingNotification function work from Windows Vista | |
70 // onwards. However even without them we will receive notifications, | |
71 // e.g. when a power source is connected. | |
72 // TODO(timvolodine) : consider polling for battery changes on windows | |
73 // versions prior to Vista, see crbug.com/402466. | |
74 power_handle_ = | |
75 RegisterNotification(&GUID_ACDC_POWER_SOURCE); | |
76 battery_change_handle_ = | |
77 RegisterNotification(&GUID_BATTERY_PERCENTAGE_REMAINING); | |
78 } else { | |
79 // Could not create a message window, execute callback with the default | |
80 // values. | |
81 callback_.Run(mojom::BatteryStatus()); | |
82 } | |
83 | |
84 UpdateNumberBatteriesHistogram(); | |
85 } | |
86 | |
87 void Stop() { | |
88 if (power_handle_) { | |
89 UnregisterNotification(power_handle_); | |
90 power_handle_ = NULL; | |
91 } | |
92 if (battery_change_handle_) { | |
93 UnregisterNotification(battery_change_handle_); | |
94 battery_change_handle_ = NULL; | |
95 } | |
96 window_.reset(); | |
97 } | |
98 | |
99 private: | |
100 void BatteryChanged() { | |
101 SYSTEM_POWER_STATUS win_status; | |
102 if (GetSystemPowerStatus(&win_status)) | |
103 callback_.Run(ComputeWebBatteryStatus(win_status)); | |
104 else | |
105 callback_.Run(mojom::BatteryStatus()); | |
106 } | |
107 | |
108 bool HandleMessage(UINT message, | |
109 WPARAM wparam, | |
110 LPARAM lparam, | |
111 LRESULT* result) { | |
112 switch(message) { | |
113 case WM_POWERBROADCAST: | |
114 if (wparam == PBT_APMPOWERSTATUSCHANGE || | |
115 wparam == PBT_POWERSETTINGCHANGE) { | |
116 BatteryChanged(); | |
117 } | |
118 *result = NULL; | |
119 return true; | |
120 default: | |
121 return false; | |
122 } | |
123 } | |
124 | |
125 HPOWERNOTIFY RegisterNotification(LPCGUID power_setting) { | |
126 if (base::win::GetVersion() < base::win::VERSION_VISTA) | |
127 return NULL; | |
128 | |
129 return RegisterPowerSettingNotification(window_->hwnd(), power_setting, | |
130 DEVICE_NOTIFY_WINDOW_HANDLE); | |
131 } | |
132 | |
133 BOOL UnregisterNotification(HPOWERNOTIFY handle) { | |
134 if (base::win::GetVersion() < base::win::VERSION_VISTA) | |
135 return FALSE; | |
136 | |
137 return UnregisterPowerSettingNotification(handle); | |
138 } | |
139 | |
140 bool CreateMessageWindow() { | |
141 // TODO(timvolodine): consider reusing the message window of PowerMonitor. | |
142 window_.reset(new base::win::MessageWindow()); | |
143 if (!window_->CreateNamed(base::Bind(&BatteryStatusObserver::HandleMessage, | |
144 base::Unretained(this)), | |
145 base::string16(kWindowClassName))) { | |
146 LOG(ERROR) << "Failed to create message window: " << kWindowClassName; | |
147 window_.reset(); | |
148 return false; | |
149 } | |
150 return true; | |
151 } | |
152 | |
153 HPOWERNOTIFY power_handle_; | |
154 HPOWERNOTIFY battery_change_handle_; | |
155 BatteryCallback callback_; | |
156 std::unique_ptr<base::win::MessageWindow> window_; | |
157 | |
158 DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver); | |
159 }; | |
160 | |
161 class BatteryStatusManagerWin : public BatteryStatusManager { | |
162 public: | |
163 explicit BatteryStatusManagerWin(const BatteryCallback& callback) | |
164 : battery_observer_(new BatteryStatusObserver(callback)) {} | |
165 ~BatteryStatusManagerWin() override { battery_observer_->Stop(); } | |
166 | |
167 public: | |
168 // BatteryStatusManager: | |
169 bool StartListeningBatteryChange() override { | |
170 battery_observer_->Start(); | |
171 return true; | |
172 } | |
173 | |
174 void StopListeningBatteryChange() override { battery_observer_->Stop(); } | |
175 | |
176 private: | |
177 std::unique_ptr<BatteryStatusObserver> battery_observer_; | |
178 | |
179 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerWin); | |
180 }; | |
181 | |
182 } // namespace | |
183 | |
184 mojom::BatteryStatus ComputeWebBatteryStatus( | |
185 const SYSTEM_POWER_STATUS& win_status) { | |
186 mojom::BatteryStatus status; | |
187 status.charging = win_status.ACLineStatus != WIN_AC_LINE_STATUS_OFFLINE; | |
188 | |
189 // Set level if available. Otherwise keep the default value which is 1. | |
190 if (win_status.BatteryLifePercent != 255) { | |
191 // Convert percentage to a value between 0 and 1 with 2 significant digits. | |
192 status.level = static_cast<double>(win_status.BatteryLifePercent) / 100.; | |
193 } | |
194 | |
195 if (!status.charging) { | |
196 // Set discharging_time if available otherwise keep the default value, | |
197 // which is +Infinity. | |
198 if (win_status.BatteryLifeTime != (DWORD)-1) | |
199 status.discharging_time = win_status.BatteryLifeTime; | |
200 status.charging_time = std::numeric_limits<double>::infinity(); | |
201 } else { | |
202 // Set charging_time to +Infinity if not fully charged, otherwise leave the | |
203 // default value, which is 0. | |
204 if (status.level < 1) | |
205 status.charging_time = std::numeric_limits<double>::infinity(); | |
206 } | |
207 return status; | |
208 } | |
209 | |
210 // static | |
211 std::unique_ptr<BatteryStatusManager> BatteryStatusManager::Create( | |
212 const BatteryStatusService::BatteryUpdateCallback& callback) { | |
213 return std::unique_ptr<BatteryStatusManager>( | |
214 new BatteryStatusManagerWin(callback)); | |
215 } | |
216 | |
217 } // namespace device | |
OLD | NEW |