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_linux.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 | |
18 namespace content { | |
19 | |
20 namespace { | |
21 | |
22 const char kUPowerServiceName[] = "org.freedesktop.UPower"; | |
23 const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device"; | |
24 const char kUPowerPath[] = "/org/freedesktop/UPower"; | |
25 const char kUPowerDeviceSignalChanged[] = "Changed"; | |
26 const char kBatteryWatcherThreadName[] = "BatteryStatusWatcher"; | |
27 const char kDBusPropertiesInterface[] = "org.freedesktop.DBus.Properties"; | |
28 const char kDBusPropertiesGetAll[] = "GetAll"; | |
29 | |
30 // UPowerDeviceType reflects the possible UPower.Device.Type values, | |
31 // see upower.freedesktop.org/docs/Device.html#Device:Type. | |
32 enum UPowerDeviceType { | |
33 UPOWER_DEVICE_TYPE_UNKNOWN = 0, | |
34 UPOWER_DEVICE_TYPE_LINE_POWER = 1, | |
35 UPOWER_DEVICE_TYPE_BATTERY = 2, | |
36 UPOWER_DEVICE_TYPE_UPS = 3, | |
37 UPOWER_DEVICE_TYPE_MONITOR = 4, | |
38 UPOWER_DEVICE_TYPE_MOUSE = 5, | |
39 UPOWER_DEVICE_TYPE_KEYBOARD = 6, | |
40 UPOWER_DEVICE_TYPE_PDA = 7, | |
41 UPOWER_DEVICE_TYPE_PHONE = 8, | |
42 }; | |
43 | |
44 // intended for uint32, int64, uint64, double. | |
hashimoto
2014/08/11 08:19:15
I see no uint64 in this file.
timvolodine
2014/08/11 16:03:00
Acknowledged.
| |
45 template<typename T> | |
46 T GetProperty(const base::DictionaryValue& dictionary, | |
hashimoto
2014/08/11 08:19:15
I don't see any merit of having this template.
It
timvolodine
2014/08/11 16:02:59
Done.
| |
47 const std::string& property_name, | |
48 T default_value) { | |
49 double value; | |
hashimoto
2014/08/11 08:19:14
Please don't leave a variable uninitialized.
timvolodine
2014/08/11 16:02:58
Done.
| |
50 return dictionary.GetDouble(property_name, &value) ? static_cast<T>(value) | |
51 : default_value; | |
52 } | |
53 | |
54 // bool | |
55 template<> | |
56 bool GetProperty(const base::DictionaryValue& dictionary, | |
57 const std::string& property_name, | |
58 bool default_value) { | |
59 bool value; | |
60 return dictionary.GetBoolean(property_name, &value) ? value : default_value; | |
61 } | |
62 | |
63 scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary( | |
64 dbus::ObjectProxy* proxy) { | |
65 dbus::MethodCall method_call(kDBusPropertiesInterface, | |
66 kDBusPropertiesGetAll); | |
hashimoto
2014/08/11 08:19:15
Why don't you use dbus/property.h?
timvolodine
2014/08/11 16:02:59
Done.
hashimoto
2014/08/12 10:01:42
Sorry for not being clear,
why don't you use dbus:
timvolodine
2014/08/12 19:27:53
I'll look into that, not sure if this will simplif
hashimoto
2014/08/14 04:17:04
ping?
| |
67 dbus::MessageWriter builder(&method_call); | |
68 builder.AppendString(kUPowerDeviceName); | |
69 | |
70 scoped_ptr<dbus::Response> response( | |
71 proxy->CallMethodAndBlock(&method_call, | |
72 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
73 if (response) { | |
74 dbus::MessageReader reader(response.get()); | |
75 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); | |
76 base::DictionaryValue* dictionary_value = NULL; | |
77 if (value && value->GetAsDictionary(&dictionary_value)) { | |
78 ignore_result(value.release()); | |
79 return scoped_ptr<base::DictionaryValue>(dictionary_value); | |
80 } | |
81 } | |
82 return scoped_ptr<base::DictionaryValue>(new base::DictionaryValue()); | |
hashimoto
2014/08/11 08:19:14
Why not returning a null scoped_ptr?
timvolodine
2014/08/11 16:02:59
upon failure it returns an empty dictionary which
hashimoto
2014/08/13 11:02:20
Why should we perform totally redandunt allocation
timvolodine
2014/08/13 16:58:08
ok made it to return null. Done.
it's actually mor
| |
83 } | |
84 | |
85 void GetPowerSourcesPaths(dbus::ObjectProxy* proxy, | |
86 std::vector<dbus::ObjectPath>& paths) { | |
hashimoto
2014/08/11 08:19:15
Please don't use non-const references.
timvolodine
2014/08/11 16:02:59
Done.
| |
87 if (!proxy) | |
88 return; | |
89 | |
90 dbus::MethodCall method_call(kUPowerServiceName, "EnumerateDevices"); | |
hashimoto
2014/08/11 08:19:15
"EnumerateDevices" should be a constant.
timvolodine
2014/08/11 16:03:00
Done.
| |
91 scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call, | |
92 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
hashimoto
2014/08/11 08:19:14
nit: Arguments should be aligned. http://google-st
timvolodine
2014/08/11 16:03:00
Done.
| |
93 | |
94 if (response) { | |
95 dbus::MessageReader reader(response.get()); | |
96 if (reader.PopArrayOfObjectPaths(&paths)) | |
97 return; | |
98 } | |
99 paths.clear(); | |
hashimoto
2014/08/11 08:19:15
This line is not needed.
timvolodine
2014/08/11 16:02:59
Done.
| |
100 } | |
101 | |
hashimoto
2014/08/11 08:19:14
Please add comments for each class which describe
timvolodine
2014/08/11 16:03:00
Done.
| |
102 class BatteryStatusNotificationThread : public base::Thread { | |
hashimoto
2014/08/11 08:19:15
No need to inherit base::Thread.
timvolodine
2014/08/11 16:02:59
? what do you mean, it's a thread.
hashimoto
2014/08/12 10:01:42
Composition is preferred to inheritance.
http://go
timvolodine
2014/08/12 19:27:53
oh I see, in this case I don't think it'll make th
hashimoto
2014/08/13 11:02:20
Please don't violate the coding style and degrade
| |
103 public: | |
104 BatteryStatusNotificationThread( | |
105 const BatteryStatusService::BatteryUpdateCallback& callback) | |
106 : base::Thread(kBatteryWatcherThreadName), | |
107 callback_(callback), | |
108 battery_proxy_(0) {} | |
hashimoto
2014/08/11 08:19:14
Use NULL.
timvolodine
2014/08/11 16:02:59
Done.
| |
109 | |
110 virtual ~BatteryStatusNotificationThread() { | |
111 if (system_bus_) | |
112 ShutdownDBusConnection(); | |
113 Stop(); | |
114 } | |
115 | |
116 void StartListening() { | |
117 DCHECK(OnWatcherThread()); | |
118 | |
119 if (system_bus_) | |
120 return; | |
121 | |
122 InitDBus(); | |
123 dbus::ObjectProxy* power_proxy = | |
124 system_bus_->GetObjectProxy(kUPowerServiceName, | |
125 dbus::ObjectPath(kUPowerPath)); | |
126 std::vector<dbus::ObjectPath> device_paths; | |
127 GetPowerSourcesPaths(power_proxy, device_paths); | |
128 | |
129 for (size_t i = 0; i < device_paths.size(); ++i) { | |
130 const dbus::ObjectPath& device_path = device_paths[i]; | |
131 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy( | |
132 kUPowerServiceName, device_path); | |
hashimoto
2014/08/11 08:19:14
ObjectProxy won't be destroyed until the bus gets
timvolodine
2014/08/11 16:02:59
Done.
| |
133 scoped_ptr<base::DictionaryValue> dictionary = | |
134 GetPropertiesAsDictionary(device_proxy); | |
135 | |
136 bool is_present = GetProperty<bool>(*dictionary, "IsPresent", false); | |
137 uint32 type = GetProperty<uint32>(*dictionary, "Type", | |
138 UPOWER_DEVICE_TYPE_UNKNOWN); | |
139 | |
140 if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) | |
141 continue; | |
142 | |
143 if (battery_proxy_) { | |
144 // TODO(timvolodine): add support for multiple batteries. Currently we | |
145 // only collect information from the first battery we encounter | |
146 // (crbug.com/400780). | |
147 // TODO(timvolodine): add UMA logging for this case. | |
148 LOG(WARNING) << "multiple batteries found, " | |
149 << "using status data of the first battery only."; | |
150 } else { | |
151 battery_proxy_ = device_proxy; | |
152 } | |
153 } | |
154 | |
155 if (!battery_proxy_) { | |
156 callback_.Run(blink::WebBatteryStatus()); | |
157 return; | |
158 } | |
159 | |
160 battery_proxy_->ConnectToSignal( | |
161 kUPowerDeviceName, | |
162 kUPowerDeviceSignalChanged, | |
163 base::Bind(&BatteryStatusNotificationThread::BatteryChanged, | |
164 base::Unretained(this)), | |
165 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected, | |
166 base::Unretained(this))); | |
167 } | |
168 | |
169 void StopListening() { | |
170 DCHECK(OnWatcherThread()); | |
171 | |
172 if (!system_bus_) | |
173 return; | |
174 | |
175 ShutdownDBusConnection(); | |
176 } | |
177 | |
178 private: | |
179 | |
hashimoto
2014/08/11 08:19:14
nit: No need to have a blank line.
timvolodine
2014/08/11 16:03:00
Done.
| |
180 bool OnWatcherThread() { | |
181 return std::string(base::PlatformThread::GetName()).compare( | |
hashimoto
2014/08/11 08:19:15
Use operator==.
timvolodine
2014/08/11 16:02:59
Acknowledged.
| |
182 kBatteryWatcherThreadName) == 0; | |
hashimoto
2014/08/11 08:19:14
Why don't you directly compare the name of the thr
timvolodine
2014/08/11 16:03:00
PlatformThread::GetName() == kBatteryWatcherThread
hashimoto
2014/08/12 10:01:42
Sorry, not then name, the ID.
Can't we do the same
timvolodine
2014/08/12 19:27:53
yes, RunsTasksOnCurrentThread appears to work. Don
| |
183 } | |
184 | |
185 void InitDBus() { | |
186 DCHECK(OnWatcherThread()); | |
187 | |
188 dbus::Bus::Options options; | |
189 options.bus_type = dbus::Bus::SYSTEM; | |
190 options.connection_type = dbus::Bus::PRIVATE; | |
191 system_bus_ = new dbus::Bus(options); | |
192 } | |
193 | |
194 void ShutdownDBusConnection() { | |
195 // Shutdown DBus connection later because there may be pending tasks on | |
196 // this thread. | |
197 message_loop()->PostTask(FROM_HERE, | |
198 base::Bind(&dbus::Bus::ShutdownAndBlock, | |
hashimoto
2014/08/11 08:19:15
Doesn't this result in a case where BatteryChanged
timvolodine
2014/08/11 16:02:59
I've added a check in BatteryChanged to prevent th
hashimoto
2014/08/12 10:01:42
I'm not sure if that |system_bus_| check can be he
timvolodine
2014/08/12 19:27:53
I am not sure I understand your concern. The Batte
hashimoto
2014/08/13 11:02:20
I was concerned about a case where this object is
timvolodine
2014/08/13 16:58:08
Done.
| |
199 system_bus_)); | |
200 system_bus_ = 0; | |
hashimoto
2014/08/11 08:19:15
Use NULL for pointers.
timvolodine
2014/08/11 16:03:00
Done.
| |
201 battery_proxy_ = 0; | |
202 } | |
203 | |
204 void OnSignalConnected(const std::string& interface_name, | |
205 const std::string& signal_name, | |
206 bool success) { | |
207 DCHECK(OnWatcherThread()); | |
208 | |
209 if (interface_name.compare(kUPowerDeviceName) != 0 || | |
210 signal_name.compare(kUPowerDeviceSignalChanged) != 0) { | |
hashimoto
2014/08/11 08:19:15
Use operator!=
timvolodine
2014/08/11 16:02:59
Done.
| |
211 return; | |
212 } | |
213 | |
214 if (!system_bus_) | |
215 return; | |
216 | |
217 if (success) { | |
218 BatteryChanged(0); | |
hashimoto
2014/08/11 08:19:15
Use NULL for pointers.
timvolodine
2014/08/11 16:02:59
Done.
| |
219 } else { | |
220 // Failed to register for "Changed" signal, execute callback with the | |
221 // default values. | |
hashimoto
2014/08/11 08:19:15
Why do we need to do this?
timvolodine
2014/08/11 16:03:00
this means we cannot get battery updates, so need
hashimoto
2014/08/12 10:01:42
It seems the Chrome OS implmentation is not doing
timvolodine
2014/08/12 19:27:53
I believe this is needed to cover the following (u
hashimoto
2014/08/13 11:02:20
When something goes wrong with D-Bus, it seems the
timvolodine
2014/08/13 16:58:08
I think ChromeOS actually forces one callback on s
hashimoto
2014/08/14 04:17:04
That RequestStatusUpdate() results in nothing if a
timvolodine
2014/08/14 22:02:45
What I meant is that callback SignalConnected() is
hashimoto
2014/08/15 02:07:00
You shouldn't expect the service to return respons
| |
222 callback_.Run(blink::WebBatteryStatus()); | |
223 } | |
224 } | |
225 | |
226 void BatteryChanged(dbus::Signal* signal /* unsused */) { | |
227 DCHECK(OnWatcherThread()); | |
228 | |
229 blink::WebBatteryStatus status; | |
230 scoped_ptr<base::DictionaryValue> dictionary = | |
231 GetPropertiesAsDictionary(battery_proxy_); | |
232 ComputeWebBatteryStatus(*dictionary, status); | |
233 | |
234 callback_.Run(status); | |
235 } | |
236 | |
237 BatteryStatusService::BatteryUpdateCallback callback_; | |
238 scoped_refptr<dbus::Bus> system_bus_; | |
239 dbus::ObjectProxy* battery_proxy_; // owned by dbus | |
hashimoto
2014/08/11 08:19:14
"owned by dbus" is ambiguos.
It's owned by the bus
timvolodine
2014/08/11 16:03:00
Done.
| |
240 | |
241 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread); | |
242 }; | |
243 | |
244 class BatteryStatusManagerLinux : public BatteryStatusManager { | |
245 public: | |
246 explicit BatteryStatusManagerLinux( | |
247 const BatteryStatusService::BatteryUpdateCallback& callback) | |
248 : callback_(callback) {} | |
249 | |
250 virtual ~BatteryStatusManagerLinux() {} | |
251 | |
252 private: | |
253 // BatteryStatusManager: | |
254 virtual bool StartListeningBatteryChange() OVERRIDE { | |
255 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
256 | |
257 if (!NotifierThreadStarted()) | |
258 return false; | |
259 | |
260 notifier_thread_->message_loop()->PostTask( | |
261 FROM_HERE, | |
262 base::Bind(&BatteryStatusNotificationThread::StartListening, | |
263 base::Unretained(notifier_thread_.get()))); | |
hashimoto
2014/08/11 08:19:14
Doesn't this result in a crash if StartListening()
timvolodine
2014/08/11 16:03:00
this cannot happen, currently the thread is destro
hashimoto
2014/08/12 10:01:42
Are you sure that StartListening() and StopListeni
timvolodine
2014/08/12 19:27:53
notifier_thead_ currently get destroyed at shutdow
| |
264 return true; | |
265 } | |
266 | |
267 virtual void StopListeningBatteryChange() OVERRIDE { | |
268 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
269 | |
270 notifier_thread_->message_loop()->PostTask( | |
271 FROM_HERE, | |
272 base::Bind(&BatteryStatusNotificationThread::StopListening, | |
273 base::Unretained(notifier_thread_.get()))); | |
274 } | |
275 | |
276 bool NotifierThreadStarted() { | |
hashimoto
2014/08/11 08:19:15
"NotifierThreadStarted" sounds like a getter which
timvolodine
2014/08/11 16:03:00
Done.
| |
277 if (notifier_thread_) | |
278 return true; | |
279 | |
280 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0); | |
281 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_)); | |
282 if (!notifier_thread_->StartWithOptions(thread_options)) { | |
283 notifier_thread_.reset(); | |
284 LOG(ERROR) << "Could not start the " << kBatteryWatcherThreadName | |
285 << " thread"; | |
286 return false; | |
287 } | |
288 return true; | |
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 void ComputeWebBatteryStatus(const base::DictionaryValue& dictionary, | |
300 blink::WebBatteryStatus& status) { | |
301 if (!dictionary.HasKey("State")) | |
302 return; | |
303 uint32 state = GetProperty<uint32>(dictionary, "State", | |
304 UPOWER_DEVICE_STATE_UNKNOWN); | |
305 status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING && | |
306 state != UPOWER_DEVICE_STATE_EMPTY; | |
307 double percentage = GetProperty<double>(dictionary, "Percentage", 100); | |
308 // Convert percentage to a value between 0 and 1 with 3 digits of precision. | |
309 status.level = round(percentage * 10) / 1000.f; | |
hashimoto
2014/08/11 08:19:14
Why should we intentionally degrade precision?
timvolodine
2014/08/11 16:02:59
this is to avoid fingerprinting and not to trigger
hashimoto
2014/08/12 10:01:42
I'm not sure what "fingerprinting" means in this c
timvolodine
2014/08/12 19:27:52
Mac and Android do not need this rounding, don't k
hashimoto
2014/08/13 11:02:20
Chrome OS is much more important than Linux.
Pleas
timvolodine
2014/08/13 16:58:08
I'll check with the chromeOS guys on this.
| |
310 | |
311 switch (state) { | |
312 case UPOWER_DEVICE_STATE_CHARGING : { | |
313 int64 time_to_full = GetProperty<int64>(dictionary, "TimeToFull", 0); | |
hashimoto
2014/08/11 08:19:15
Why do we need to convert a double value stored in
timvolodine
2014/08/11 16:02:59
Done.
| |
314 status.chargingTime = | |
315 (time_to_full > 0) ? time_to_full | |
316 : std::numeric_limits<double>::infinity(); | |
317 break; | |
318 } | |
319 case UPOWER_DEVICE_STATE_DISCHARGING : { | |
320 int64 time_to_empty = GetProperty<int64>(dictionary, "TimeToEmpty", 0); | |
321 // Set dischargingTime if it's available. Otherwise leave the default | |
322 // value which is +infinity. | |
323 if (time_to_empty > 0) | |
324 status.dischargingTime = time_to_empty; | |
325 status.chargingTime = std::numeric_limits<double>::infinity(); | |
326 break; | |
327 } | |
328 case UPOWER_DEVICE_STATE_FULL : { | |
329 break; | |
330 } | |
331 default: { | |
332 status.chargingTime = std::numeric_limits<double>::infinity(); | |
333 } | |
334 } | |
335 } | |
336 | |
337 // static | |
338 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( | |
339 const BatteryStatusService::BatteryUpdateCallback& callback) { | |
340 return scoped_ptr<BatteryStatusManager>( | |
341 new BatteryStatusManagerLinux(callback)); | |
342 } | |
343 | |
344 } // namespace content | |
OLD | NEW |