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.h" | |
6 | |
7 #include "base/macros.h" | |
8 #include "base/threading/thread.h" | |
9 #include "base/values.h" | |
10 #include "content/public/browser/browser_thread.h" | |
11 #include "dbus/bus.h" | |
12 #include "dbus/message.h" | |
13 #include "dbus/object_path.h" | |
14 #include "dbus/object_proxy.h" | |
15 #include "dbus/values_util.h" | |
16 #include "third_party/WebKit/public/platform/WebBatteryStatus.h" | |
17 #include "third_party/cros_system_api/dbus/service_constants.h" | |
mlamouri (slow - plz ping)
2014/08/01 17:14:14
I think you should remove that #include. Isn't it
timvolodine
2014/08/06 16:37:42
Done.
| |
18 | |
19 namespace content { | |
20 | |
21 namespace { | |
22 | |
23 const char kUPowerServiceName[] = "org.freedesktop.UPower"; | |
24 const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device"; | |
25 const char kUPowerPath[] = "/org/freedesktop/UPower"; | |
26 const char kUPowerDeviceSignalChanged[] = "Changed"; | |
mlamouri (slow - plz ping)
2014/08/01 17:14:13
I think you can use "DeviceChanged" instead of "Ch
timvolodine
2014/08/06 16:37:42
UPower.Device only has the Changed() signal, in li
mlamouri (slow - plz ping)
2014/08/07 16:17:04
My bad, I misread. I didn't see that you were regi
| |
27 const char kBatteryWatcherThreadName[] = "BatteryStatusWatcher"; | |
28 | |
29 // intended for unit32, int64, uint64, double. | |
30 template<typename T> | |
31 T GetProperty(const base::DictionaryValue& dictionary, | |
32 const std::string& property_name, | |
33 T default_value) { | |
34 double value; | |
35 return dictionary.GetDouble(property_name, &value) ? value : default_value; | |
36 } | |
37 | |
38 // bool | |
39 template<> | |
40 bool GetProperty(const base::DictionaryValue& dictionary, | |
41 const std::string& property_name, | |
42 bool default_value) { | |
43 bool value; | |
44 return dictionary.GetBoolean(property_name, &value) ? value : default_value; | |
45 } | |
46 | |
47 scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary( | |
48 dbus::ObjectProxy* proxy) { | |
49 dbus::MethodCall method_call(dbus::kDBusPropertiesInterface, | |
50 dbus::kDBusPropertiesGetAll); | |
51 dbus::MessageWriter builder(&method_call); | |
52 builder.AppendString(kUPowerDeviceName); | |
53 | |
54 scoped_ptr<dbus::Response> response( | |
55 proxy->CallMethodAndBlock(&method_call, | |
56 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
57 if (response) { | |
58 dbus::MessageReader reader(response.get()); | |
59 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); | |
60 base::DictionaryValue* dictionary_value = NULL; | |
61 if (value && value->GetAsDictionary(&dictionary_value)) { | |
62 ignore_result(value.release()); | |
63 return scoped_ptr<base::DictionaryValue>(dictionary_value); | |
64 } | |
65 } | |
66 return scoped_ptr<base::DictionaryValue>(new base::DictionaryValue()); | |
67 } | |
68 | |
69 void GetPowerSourcesPaths(dbus::ObjectProxy* proxy, | |
70 std::vector<dbus::ObjectPath>& paths) { | |
71 if (!proxy) | |
72 return; | |
73 | |
74 dbus::MethodCall method_call(kUPowerServiceName, "EnumerateDevices"); | |
75 scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call, | |
76 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
77 | |
78 if (response) { | |
79 dbus::MessageReader reader(response.get()); | |
80 if (reader.PopArrayOfObjectPaths(&paths)) | |
81 return; | |
82 } | |
83 paths.clear(); | |
84 } | |
85 | |
86 class BatteryStatusNotificationThread : public base::Thread { | |
87 public: | |
88 BatteryStatusNotificationThread( | |
89 const BatteryStatusService::BatteryUpdateCallback& callback) | |
90 : base::Thread(kBatteryWatcherThreadName), | |
91 callback_(callback), | |
92 battery_proxy_(0) {} | |
93 | |
94 virtual ~BatteryStatusNotificationThread() { | |
95 if (system_bus_) | |
96 ShutdownDBus(); | |
97 Stop(); | |
98 } | |
99 | |
100 void StartListening() { | |
101 DCHECK(OnWatcherThread()); | |
102 | |
103 if (system_bus_) | |
104 return; | |
105 | |
106 InitDBus(); | |
107 dbus::ObjectProxy* power_proxy = | |
108 system_bus_->GetObjectProxy(kUPowerServiceName, | |
109 dbus::ObjectPath(kUPowerPath)); | |
110 std::vector<dbus::ObjectPath> device_paths; | |
111 GetPowerSourcesPaths(power_proxy, device_paths); | |
112 | |
113 for (size_t i = 0; i < device_paths.size(); ++i) { | |
114 const dbus::ObjectPath& device_path = device_paths[i]; | |
115 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy( | |
116 kUPowerServiceName, device_path); | |
117 scoped_ptr<base::DictionaryValue> dictionary = | |
118 GetPropertiesAsDictionary(device_proxy); | |
119 | |
120 bool is_present = GetProperty<bool>(*dictionary, "IsPresent", false); | |
121 uint32 type = GetProperty<uint32>(*dictionary, "Type", 0); | |
122 | |
123 if (is_present && type == 2 /* battery */) { | |
124 if (battery_proxy_) { | |
125 // TODO(timvolodine): add support for multiple batteries. Currently we | |
Michael van Ouwerkerk
2014/08/01 15:24:39
Please file a bug for this. It would be nice to ha
timvolodine
2014/08/06 16:37:42
Done.
| |
126 // only collect information from the first battery we encounter. | |
127 LOG(WARNING) << "multiple batteries found, " | |
128 << "using status data of the first battery only."; | |
129 } else { | |
130 battery_proxy_ = device_proxy; | |
131 } | |
132 } | |
133 } | |
134 | |
135 if (!battery_proxy_) { | |
136 callback_.Run(blink::WebBatteryStatus()); | |
137 return; | |
138 } | |
139 | |
140 battery_proxy_->ConnectToSignal( | |
141 kUPowerDeviceName, | |
142 kUPowerDeviceSignalChanged, | |
143 base::Bind(&BatteryStatusNotificationThread::BatteryChanged, | |
144 base::Unretained(this)), | |
145 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected, | |
146 base::Unretained(this))); | |
147 } | |
148 | |
149 void StopListening() { | |
150 DCHECK(OnWatcherThread()); | |
151 | |
152 if (!system_bus_) | |
153 return; | |
154 | |
155 ShutdownDBus(); | |
mlamouri (slow - plz ping)
2014/08/01 17:14:13
nit: could you rename this ShutdownDBusConnection(
timvolodine
2014/08/06 16:37:42
Done.
| |
156 } | |
157 | |
158 private: | |
159 | |
160 bool OnWatcherThread() { | |
161 return std::string(base::PlatformThread::GetName()).compare( | |
162 kBatteryWatcherThreadName) == 0; | |
163 } | |
164 | |
165 void InitDBus() { | |
166 DCHECK(OnWatcherThread()); | |
167 | |
168 dbus::Bus::Options options; | |
169 options.bus_type = dbus::Bus::SYSTEM; | |
170 options.connection_type = dbus::Bus::PRIVATE; | |
171 system_bus_ = new dbus::Bus(options); | |
172 } | |
173 | |
174 void ShutdownDBus() { | |
175 // Shutdown DBus connection later because there may be pending tasks on | |
176 // this thread. | |
177 message_loop()->PostTask(FROM_HERE, | |
178 base::Bind(&dbus::Bus::ShutdownAndBlock, | |
179 system_bus_)); | |
180 system_bus_ = 0; | |
181 battery_proxy_ = 0; | |
182 } | |
183 | |
184 void OnSignalConnected(const std::string& interface_name, | |
185 const std::string& signal_name, | |
186 bool success) { | |
187 DCHECK(OnWatcherThread()); | |
188 | |
189 if (interface_name.compare(kUPowerDeviceName) != 0 || | |
190 signal_name.compare(kUPowerDeviceSignalChanged) != 0) { | |
191 return; | |
192 } | |
193 | |
194 if (!system_bus_) | |
195 return; | |
196 | |
197 if (success) { | |
198 BatteryChanged(0); | |
199 } else { | |
200 // Failed to register for "Changed" signal, execute callback with the | |
201 // default values. | |
202 callback_.Run(blink::WebBatteryStatus()); | |
203 } | |
204 } | |
205 | |
206 void BatteryChanged(dbus::Signal* signal) { | |
Michael van Ouwerkerk
2014/08/01 15:24:39
Maybe a unit test would be appropriate here. I thi
Michael van Ouwerkerk
2014/08/01 15:24:39
The signal param initially confused me. How about
timvolodine
2014/08/06 16:37:42
Done.
timvolodine
2014/08/06 16:37:42
I've extracted a method for testing (ComputeWebBat
| |
207 DCHECK(OnWatcherThread()); | |
208 | |
209 blink::WebBatteryStatus status; | |
210 scoped_ptr<base::DictionaryValue> dictionary = | |
211 GetPropertiesAsDictionary(battery_proxy_); | |
212 | |
213 uint32 state = GetProperty<uint32>(*dictionary, "State", -1); | |
214 status.charging = state != 2; // true if not discharging | |
mlamouri (slow - plz ping)
2014/08/01 17:14:14
Could you have an enum that mirror the possible va
timvolodine
2014/08/06 16:37:42
Done.
| |
215 double percentage = GetProperty<double>(*dictionary, "Percentage", 1); | |
216 // Convert percentage to a value between 0 and 1 with 3 digits of precision. | |
217 status.level = round(percentage * 10) / 1000.f; | |
mlamouri (slow - plz ping)
2014/08/01 17:14:13
Why not *0.01.f or /10.f?
timvolodine
2014/08/06 16:37:42
dbus reports the value with lost of fractional dig
| |
218 | |
219 switch (state) { | |
220 case 1 : { // charging | |
221 int64 time_to_full = GetProperty<int64>(*dictionary, "TimeToFull", 0); | |
222 status.chargingTime = | |
mlamouri (slow - plz ping)
2014/08/01 17:14:13
Are we using the right default value in Blink if t
timvolodine
2014/08/06 16:37:42
here we explicitly set it to either the time or +i
| |
223 (time_to_full > 0) ? time_to_full | |
224 : std::numeric_limits<double>::infinity(); | |
225 break; | |
226 } | |
227 case 2 : { // discharging | |
228 int64 time_to_empty = GetProperty<int64>(*dictionary, "TimeToEmpty", 0); | |
229 // Set dischargingTime if it's available. Otherwise leave the default | |
230 // value which is +infinity. | |
231 if (time_to_empty > 0) | |
232 status.dischargingTime = time_to_empty; | |
mlamouri (slow - plz ping)
2014/08/01 17:14:13
ditto
timvolodine
2014/08/06 16:37:43
dischargingTime is +inf by default so we are fine
| |
233 status.chargingTime = std::numeric_limits<double>::infinity(); | |
234 break; | |
235 } | |
236 case 4 : { // fully charged | |
237 break; | |
238 } | |
239 default: { | |
240 status.chargingTime = std::numeric_limits<double>::infinity(); | |
241 } | |
242 } | |
243 | |
244 callback_.Run(status); | |
245 } | |
246 | |
247 BatteryStatusService::BatteryUpdateCallback callback_; | |
248 scoped_refptr<dbus::Bus> system_bus_; | |
249 dbus::ObjectProxy* battery_proxy_; // owned by dbus | |
250 | |
251 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread); | |
252 }; | |
253 | |
254 class BatteryStatusManagerLinux : public BatteryStatusManager { | |
255 public: | |
256 explicit BatteryStatusManagerLinux( | |
257 const BatteryStatusService::BatteryUpdateCallback& callback) | |
258 : callback_(callback) {} | |
259 | |
260 virtual ~BatteryStatusManagerLinux() {} | |
261 | |
262 private: | |
263 // BatteryStatusManager: | |
264 virtual bool StartListeningBatteryChange() OVERRIDE { | |
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
266 if (!notifier_thread_) { | |
267 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0); | |
268 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_)); | |
269 if (!notifier_thread_->StartWithOptions(thread_options)) { | |
270 notifier_thread_.reset(); | |
271 return false; | |
mlamouri (slow - plz ping)
2014/08/01 17:14:14
That's not really something we expect to happen, r
timvolodine
2014/08/06 16:37:42
right, added LOG(ERROR).
| |
272 } | |
273 } | |
274 | |
275 notifier_thread_->message_loop()->PostTask( | |
276 FROM_HERE, | |
277 base::Bind(&BatteryStatusNotificationThread::StartListening, | |
278 base::Unretained(notifier_thread_.get()))); | |
279 return true; | |
280 } | |
281 | |
282 virtual void StopListeningBatteryChange() OVERRIDE { | |
283 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
284 | |
285 notifier_thread_->message_loop()->PostTask( | |
286 FROM_HERE, | |
287 base::Bind(&BatteryStatusNotificationThread::StopListening, | |
288 base::Unretained(notifier_thread_.get()))); | |
289 } | |
290 | |
291 BatteryStatusService::BatteryUpdateCallback callback_; | |
292 scoped_ptr<BatteryStatusNotificationThread> notifier_thread_; | |
293 | |
294 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux); | |
295 }; | |
296 | |
297 } // namespace | |
298 | |
299 // static | |
300 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( | |
301 const BatteryStatusService::BatteryUpdateCallback& callback) { | |
302 return scoped_ptr<BatteryStatusManager>( | |
303 new BatteryStatusManagerLinux(callback)); | |
304 } | |
305 | |
306 } // namespace content | |
OLD | NEW |