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