Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(603)

Side by Side Diff: content/browser/battery_status/battery_status_manager_linux.cc

Issue 436683002: Battery Status API: implementation for Linux. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix BUILD.gn Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698