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