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 // intended for unit32, int64, uint64, double. | |
Michael van Ouwerkerk
2014/08/08 14:54:59
s/unit32/uint32/
timvolodine
2014/08/08 18:46:45
Done.
| |
31 template<typename T> | |
32 T GetProperty(const base::DictionaryValue& dictionary, | |
33 const std::string& property_name, | |
34 T default_value) { | |
35 double value; | |
36 return dictionary.GetDouble(property_name, &value) ? value : default_value; | |
stevenjb
2014/08/08 16:18:16
static_cast<T>(value)
timvolodine
2014/08/08 18:46:45
Done.
| |
37 } | |
38 | |
39 // bool | |
40 template<> | |
41 bool GetProperty(const base::DictionaryValue& dictionary, | |
42 const std::string& property_name, | |
43 bool default_value) { | |
44 bool value; | |
45 return dictionary.GetBoolean(property_name, &value) ? value : default_value; | |
46 } | |
47 | |
48 scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary( | |
49 dbus::ObjectProxy* proxy) { | |
50 dbus::MethodCall method_call(kDBusPropertiesInterface, | |
51 kDBusPropertiesGetAll); | |
52 dbus::MessageWriter builder(&method_call); | |
53 builder.AppendString(kUPowerDeviceName); | |
54 | |
55 scoped_ptr<dbus::Response> response( | |
56 proxy->CallMethodAndBlock(&method_call, | |
57 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
58 if (response) { | |
stevenjb
2014/08/08 16:18:16
We should probably return NULL on failure to read
timvolodine
2014/08/08 18:46:45
the idea here is to return an empty dictionary, wh
| |
59 dbus::MessageReader reader(response.get()); | |
60 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); | |
61 base::DictionaryValue* dictionary_value = NULL; | |
62 if (value && value->GetAsDictionary(&dictionary_value)) { | |
63 ignore_result(value.release()); | |
64 return scoped_ptr<base::DictionaryValue>(dictionary_value); | |
65 } | |
66 } | |
67 return scoped_ptr<base::DictionaryValue>(new base::DictionaryValue()); | |
68 } | |
69 | |
70 void GetPowerSourcesPaths(dbus::ObjectProxy* proxy, | |
71 std::vector<dbus::ObjectPath>& paths) { | |
72 if (!proxy) | |
73 return; | |
74 | |
75 dbus::MethodCall method_call(kUPowerServiceName, "EnumerateDevices"); | |
76 scoped_ptr<dbus::Response> response(proxy->CallMethodAndBlock(&method_call, | |
77 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); | |
78 | |
79 if (response) { | |
80 dbus::MessageReader reader(response.get()); | |
81 if (reader.PopArrayOfObjectPaths(&paths)) | |
82 return; | |
83 } | |
84 paths.clear(); | |
85 } | |
86 | |
87 class BatteryStatusNotificationThread : public base::Thread { | |
88 public: | |
89 BatteryStatusNotificationThread( | |
90 const BatteryStatusService::BatteryUpdateCallback& callback) | |
91 : base::Thread(kBatteryWatcherThreadName), | |
92 callback_(callback), | |
93 battery_proxy_(0) {} | |
94 | |
95 virtual ~BatteryStatusNotificationThread() { | |
96 if (system_bus_) | |
97 ShutdownDBusConnection(); | |
98 Stop(); | |
99 } | |
100 | |
101 void StartListening() { | |
102 DCHECK(OnWatcherThread()); | |
103 | |
104 if (system_bus_) | |
105 return; | |
106 | |
107 InitDBus(); | |
108 dbus::ObjectProxy* power_proxy = | |
109 system_bus_->GetObjectProxy(kUPowerServiceName, | |
110 dbus::ObjectPath(kUPowerPath)); | |
111 std::vector<dbus::ObjectPath> device_paths; | |
112 GetPowerSourcesPaths(power_proxy, device_paths); | |
113 | |
114 for (size_t i = 0; i < device_paths.size(); ++i) { | |
115 const dbus::ObjectPath& device_path = device_paths[i]; | |
116 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy( | |
117 kUPowerServiceName, device_path); | |
118 scoped_ptr<base::DictionaryValue> dictionary = | |
119 GetPropertiesAsDictionary(device_proxy); | |
120 | |
121 bool is_present = GetProperty<bool>(*dictionary, "IsPresent", false); | |
122 uint32 type = GetProperty<uint32>(*dictionary, "Type", 0); | |
123 | |
124 if (is_present && type == 2 /* battery */) { | |
mlamouri (slow - plz ping)
2014/08/07 16:17:04
Could you add an enum for that too? Sorry for not
stevenjb
2014/08/08 16:18:16
nit: if (!is_present || type != TYPE_BATTERY) cont
timvolodine
2014/08/08 18:46:45
Done.
timvolodine
2014/08/08 18:46:45
Done.
| |
125 if (battery_proxy_) { | |
126 // TODO(timvolodine): add support for multiple batteries. Currently we | |
127 // only collect information from the first battery we encounter | |
128 // (crbug.com/400780). | |
129 // TODO(timvolodine): add UMA logging for this case. | |
130 LOG(WARNING) << "multiple batteries found, " | |
131 << "using status data of the first battery only."; | |
132 } else { | |
133 battery_proxy_ = device_proxy; | |
134 } | |
135 } | |
136 } | |
137 | |
138 if (!battery_proxy_) { | |
139 callback_.Run(blink::WebBatteryStatus()); | |
140 return; | |
141 } | |
142 | |
143 battery_proxy_->ConnectToSignal( | |
144 kUPowerDeviceName, | |
145 kUPowerDeviceSignalChanged, | |
146 base::Bind(&BatteryStatusNotificationThread::BatteryChanged, | |
147 base::Unretained(this)), | |
148 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected, | |
149 base::Unretained(this))); | |
150 } | |
151 | |
152 void StopListening() { | |
153 DCHECK(OnWatcherThread()); | |
154 | |
155 if (!system_bus_) | |
156 return; | |
157 | |
158 ShutdownDBusConnection(); | |
159 } | |
160 | |
161 private: | |
162 | |
163 bool OnWatcherThread() { | |
164 return std::string(base::PlatformThread::GetName()).compare( | |
165 kBatteryWatcherThreadName) == 0; | |
166 } | |
167 | |
168 void InitDBus() { | |
169 DCHECK(OnWatcherThread()); | |
170 | |
171 dbus::Bus::Options options; | |
172 options.bus_type = dbus::Bus::SYSTEM; | |
173 options.connection_type = dbus::Bus::PRIVATE; | |
174 system_bus_ = new dbus::Bus(options); | |
175 } | |
176 | |
177 void ShutdownDBusConnection() { | |
178 // Shutdown DBus connection later because there may be pending tasks on | |
179 // this thread. | |
180 message_loop()->PostTask(FROM_HERE, | |
181 base::Bind(&dbus::Bus::ShutdownAndBlock, | |
182 system_bus_)); | |
183 system_bus_ = 0; | |
184 battery_proxy_ = 0; | |
185 } | |
186 | |
187 void OnSignalConnected(const std::string& interface_name, | |
188 const std::string& signal_name, | |
189 bool success) { | |
190 DCHECK(OnWatcherThread()); | |
191 | |
192 if (interface_name.compare(kUPowerDeviceName) != 0 || | |
193 signal_name.compare(kUPowerDeviceSignalChanged) != 0) { | |
194 return; | |
195 } | |
196 | |
197 if (!system_bus_) | |
198 return; | |
199 | |
200 if (success) { | |
201 BatteryChanged(0); | |
202 } else { | |
203 // Failed to register for "Changed" signal, execute callback with the | |
204 // default values. | |
205 callback_.Run(blink::WebBatteryStatus()); | |
206 } | |
207 } | |
208 | |
209 void BatteryChanged(dbus::Signal* signal /* unsused */) { | |
210 DCHECK(OnWatcherThread()); | |
211 blink::WebBatteryStatus status; | |
212 scoped_ptr<base::DictionaryValue> dictionary = | |
213 GetPropertiesAsDictionary(battery_proxy_); | |
214 ComputeWebBatteryStatus(*dictionary, status); | |
215 callback_.Run(status); | |
mlamouri (slow - plz ping)
2014/08/07 16:17:04
This block needs some empty lines. It's quite dens
timvolodine
2014/08/08 18:46:45
Done.
| |
216 } | |
217 | |
218 BatteryStatusService::BatteryUpdateCallback callback_; | |
219 scoped_refptr<dbus::Bus> system_bus_; | |
220 dbus::ObjectProxy* battery_proxy_; // owned by dbus | |
221 | |
222 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread); | |
223 }; | |
224 | |
225 class BatteryStatusManagerLinux : public BatteryStatusManager { | |
226 public: | |
227 explicit BatteryStatusManagerLinux( | |
228 const BatteryStatusService::BatteryUpdateCallback& callback) | |
229 : callback_(callback) {} | |
230 | |
231 virtual ~BatteryStatusManagerLinux() {} | |
232 | |
233 private: | |
234 // BatteryStatusManager: | |
235 virtual bool StartListeningBatteryChange() OVERRIDE { | |
236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
237 if (!notifier_thread_) { | |
238 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0); | |
239 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_)); | |
240 if (!notifier_thread_->StartWithOptions(thread_options)) { | |
241 notifier_thread_.reset(); | |
242 LOG(ERROR) << "Could not start the " << kBatteryWatcherThreadName | |
243 << "thread"; | |
244 return false; | |
245 } | |
246 } | |
stevenjb
2014/08/08 16:18:16
This bit should probably be moved to a helper meth
timvolodine
2014/08/08 18:46:45
Done.
| |
247 | |
248 notifier_thread_->message_loop()->PostTask( | |
249 FROM_HERE, | |
250 base::Bind(&BatteryStatusNotificationThread::StartListening, | |
251 base::Unretained(notifier_thread_.get()))); | |
252 return true; | |
253 } | |
254 | |
255 virtual void StopListeningBatteryChange() OVERRIDE { | |
256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
257 | |
258 notifier_thread_->message_loop()->PostTask( | |
259 FROM_HERE, | |
260 base::Bind(&BatteryStatusNotificationThread::StopListening, | |
261 base::Unretained(notifier_thread_.get()))); | |
262 } | |
263 | |
264 BatteryStatusService::BatteryUpdateCallback callback_; | |
265 scoped_ptr<BatteryStatusNotificationThread> notifier_thread_; | |
266 | |
267 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux); | |
268 }; | |
269 | |
270 } // namespace | |
271 | |
272 // Computes and sets the WebBatteryStatus values where possible based on the | |
273 // passed dictionary. | |
274 void ComputeWebBatteryStatus(const base::DictionaryValue& dictionary, | |
275 blink::WebBatteryStatus& status) { | |
276 if (!dictionary.HasKey("State")) | |
277 return; | |
278 uint32 state = GetProperty<uint32>(dictionary, "State", | |
279 UPOWER_DEVICE_STATE_UNKNOWN); | |
280 status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING; | |
281 double percentage = GetProperty<double>(dictionary, "Percentage", 100); | |
282 // Convert percentage to a value between 0 and 1 with 3 digits of precision. | |
283 status.level = round(percentage * 10) / 1000.f; | |
mlamouri (slow - plz ping)
2014/08/07 16:17:04
Could you add a test with "Percentage" = 13.37 and
timvolodine
2014/08/08 18:46:45
Done.
| |
284 | |
285 switch (state) { | |
286 case UPOWER_DEVICE_STATE_CHARGING : { | |
287 int64 time_to_full = GetProperty<int64>(dictionary, "TimeToFull", 0); | |
288 status.chargingTime = | |
289 (time_to_full > 0) ? time_to_full | |
290 : std::numeric_limits<double>::infinity(); | |
291 break; | |
292 } | |
293 case UPOWER_DEVICE_STATE_DISCHARGING : { | |
294 int64 time_to_empty = GetProperty<int64>(dictionary, "TimeToEmpty", 0); | |
295 // Set dischargingTime if it's available. Otherwise leave the default | |
296 // value which is +infinity. | |
297 if (time_to_empty > 0) | |
298 status.dischargingTime = time_to_empty; | |
299 status.chargingTime = std::numeric_limits<double>::infinity(); | |
300 break; | |
301 } | |
302 case UPOWER_DEVICE_STATE_FULL : { | |
303 break; | |
304 } | |
305 default: { | |
306 status.chargingTime = std::numeric_limits<double>::infinity(); | |
307 } | |
308 } | |
309 } | |
310 | |
311 // static | |
312 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( | |
313 const BatteryStatusService::BatteryUpdateCallback& callback) { | |
314 return scoped_ptr<BatteryStatusManager>( | |
315 new BatteryStatusManagerLinux(callback)); | |
316 } | |
317 | |
318 } // namespace content | |
OLD | NEW |