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

Side by Side Diff: device/battery/battery_status_manager_linux.cc

Issue 2066503002: Implement device::BatteryStatus support for UPower daemon 0.99.x (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fixup! Implement device::BatteryStatus support for UPower daemon 0.99.x Created 4 years, 5 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "device/battery/battery_status_manager_linux.h" 5 #include "device/battery/battery_status_manager_linux.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <limits>
10 #include <memory> 11 #include <memory>
12 #include <string>
13 #include <vector>
11 14
12 #include "base/macros.h" 15 #include "base/macros.h"
13 #include "base/metrics/histogram.h" 16 #include "base/metrics/histogram.h"
14 #include "base/threading/thread.h" 17 #include "base/threading/thread.h"
15 #include "base/values.h" 18 #include "base/values.h"
19 #include "base/version.h"
16 #include "dbus/bus.h" 20 #include "dbus/bus.h"
17 #include "dbus/message.h" 21 #include "dbus/message.h"
18 #include "dbus/object_path.h" 22 #include "dbus/object_path.h"
19 #include "dbus/object_proxy.h" 23 #include "dbus/object_proxy.h"
20 #include "dbus/property.h" 24 #include "dbus/property.h"
21 #include "dbus/values_util.h" 25 #include "dbus/values_util.h"
22 #include "device/battery/battery_status_manager.h" 26 #include "device/battery/battery_status_manager_linux-inl.h"
23 27
24 namespace device { 28 namespace device {
25
26 namespace { 29 namespace {
27
28 const char kUPowerServiceName[] = "org.freedesktop.UPower";
29 const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
30 const char kUPowerPath[] = "/org/freedesktop/UPower";
31 const char kUPowerDeviceSignalChanged[] = "Changed";
32 const char kUPowerEnumerateDevices[] = "EnumerateDevices";
33 const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier"; 30 const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier";
34 31
35 // UPowerDeviceType reflects the possible UPower.Device.Type values, 32 class UPowerProperties : public dbus::PropertySet {
36 // see upower.freedesktop.org/docs/Device.html#Device:Type. 33 public:
37 enum UPowerDeviceType { 34 UPowerProperties(dbus::ObjectProxy* object_proxy,
38 UPOWER_DEVICE_TYPE_UNKNOWN = 0, 35 const PropertyChangedCallback callback);
39 UPOWER_DEVICE_TYPE_LINE_POWER = 1, 36 ~UPowerProperties() override;
40 UPOWER_DEVICE_TYPE_BATTERY = 2, 37
41 UPOWER_DEVICE_TYPE_UPS = 3, 38 base::Version daemon_version();
42 UPOWER_DEVICE_TYPE_MONITOR = 4, 39
43 UPOWER_DEVICE_TYPE_MOUSE = 5, 40 private:
44 UPOWER_DEVICE_TYPE_KEYBOARD = 6, 41 dbus::Property<std::string> daemon_version_;
45 UPOWER_DEVICE_TYPE_PDA = 7, 42
46 UPOWER_DEVICE_TYPE_PHONE = 8, 43 DISALLOW_COPY_AND_ASSIGN(UPowerProperties);
47 }; 44 };
48 45
49 typedef std::vector<dbus::ObjectPath> PathsVector; 46 UPowerProperties::UPowerProperties(dbus::ObjectProxy* object_proxy,
50 47 const PropertyChangedCallback callback)
51 double GetPropertyAsDouble(const base::DictionaryValue& dictionary, 48 : dbus::PropertySet(object_proxy, kUPowerDeviceInterfaceName, callback) {
52 const std::string& property_name, 49 RegisterProperty(kUPowerPropertyDaemonVersion, &daemon_version_);
53 double default_value) {
54 double value = default_value;
55 return dictionary.GetDouble(property_name, &value) ? value : default_value;
56 } 50 }
57 51
58 bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary, 52 UPowerProperties::~UPowerProperties() {}
59 const std::string& property_name, 53
60 bool default_value) { 54 base::Version UPowerProperties::daemon_version() {
61 bool value = default_value; 55 return (daemon_version_.is_valid() || daemon_version_.GetAndBlock())
62 return dictionary.GetBoolean(property_name, &value) ? value : default_value; 56 ? base::Version(daemon_version_.value())
57 : base::Version();
63 } 58 }
64 59
65 std::unique_ptr<base::DictionaryValue> GetPropertiesAsDictionary( 60 class UPowerObject {
66 dbus::ObjectProxy* proxy) { 61 public:
67 dbus::MethodCall method_call(dbus::kPropertiesInterface, 62 typedef dbus::PropertySet::PropertyChangedCallback PropertyChangedCallback;
68 dbus::kPropertiesGetAll);
69 dbus::MessageWriter builder(&method_call);
70 builder.AppendString(kUPowerDeviceName);
71 63
72 std::unique_ptr<dbus::Response> response(proxy->CallMethodAndBlock( 64 UPowerObject(dbus::Bus* dbus,
73 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); 65 const PropertyChangedCallback property_changed_callback);
74 if (response) { 66 ~UPowerObject();
75 dbus::MessageReader reader(response.get()); 67
76 std::unique_ptr<base::Value> value(dbus::PopDataAsValue(&reader)); 68 std::vector<dbus::ObjectPath> EnumerateDevices();
77 base::DictionaryValue* dictionary_value = NULL; 69 dbus::ObjectPath GetDisplayDevice();
78 if (value && value->GetAsDictionary(&dictionary_value)) { 70
79 ignore_result(value.release()); 71 dbus::ObjectProxy* proxy() { return proxy_; }
80 return std::unique_ptr<base::DictionaryValue>(dictionary_value); 72 UPowerProperties* properties() { return properties_.get(); }
81 } 73
82 } 74 private:
83 return std::unique_ptr<base::DictionaryValue>(); 75 dbus::Bus* dbus_; // Owned by the BatteryStatusNotificationThread.
76 dbus::ObjectProxy* proxy_; // Owned by the dbus.
77 std::unique_ptr<UPowerProperties> properties_;
78
79 DISALLOW_COPY_AND_ASSIGN(UPowerObject);
80 };
81
82 UPowerObject::UPowerObject(
83 dbus::Bus* dbus,
84 const PropertyChangedCallback property_changed_callback)
85 : dbus_(dbus),
86 proxy_(dbus_->GetObjectProxy(kUPowerServiceName,
87 dbus::ObjectPath(kUPowerPath))),
88 properties_(new UPowerProperties(proxy_, property_changed_callback)) {}
89
90 UPowerObject::~UPowerObject() {
91 properties_.reset(); // before the proxy is deleted.
92 dbus_->RemoveObjectProxy(kUPowerServiceName, proxy_->object_path(),
93 base::Bind(&base::DoNothing));
84 } 94 }
85 95
86 std::unique_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) { 96 std::vector<dbus::ObjectPath> UPowerObject::EnumerateDevices() {
87 std::unique_ptr<PathsVector> paths(new PathsVector()); 97 std::vector<dbus::ObjectPath> paths;
88 if (!proxy) 98 dbus::MethodCall method_call(kUPowerServiceName,
89 return paths; 99 kUPowerMethodEnumerateDevices);
90 100 std::unique_ptr<dbus::Response> response(proxy_->CallMethodAndBlock(
91 dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices);
92 std::unique_ptr<dbus::Response> response(proxy->CallMethodAndBlock(
93 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT)); 101 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
94 102
95 if (response) { 103 if (response) {
96 dbus::MessageReader reader(response.get()); 104 dbus::MessageReader reader(response.get());
97 reader.PopArrayOfObjectPaths(paths.get()); 105 reader.PopArrayOfObjectPaths(&paths);
98 } 106 }
99 return paths; 107 return paths;
100 } 108 }
101 109
110 dbus::ObjectPath UPowerObject::GetDisplayDevice() {
111 dbus::ObjectPath display_device_path;
112 if (!proxy_)
113 return display_device_path;
114
115 dbus::MethodCall method_call(kUPowerServiceName,
116 kUPowerMethodGetDisplayDevice);
117 std::unique_ptr<dbus::Response> response(proxy_->CallMethodAndBlock(
118 &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
119
120 if (response) {
121 dbus::MessageReader reader(response.get());
122 reader.PopObjectPath(&display_device_path);
123 }
124 return display_device_path;
125 }
126
102 void UpdateNumberBatteriesHistogram(int count) { 127 void UpdateNumberBatteriesHistogram(int count) {
103 UMA_HISTOGRAM_CUSTOM_COUNTS( 128 UMA_HISTOGRAM_CUSTOM_COUNTS(
104 "BatteryStatus.NumberBatteriesLinux", count, 1, 5, 6); 129 "BatteryStatus.NumberBatteriesLinux", count, 1, 5, 6);
105 } 130 }
106 131
132 class BatteryProperties : public dbus::PropertySet {
133 public:
134 BatteryProperties(dbus::ObjectProxy* object_proxy,
135 const PropertyChangedCallback callback);
136 ~BatteryProperties() override;
137
138 void ConnectSignals() override;
139
140 void Invalidate();
141
142 bool is_present(bool default_value = false);
143 double percentage(double default_value = 100);
144 uint32_t state(uint32_t default_value = UPOWER_DEVICE_STATE_UNKNOWN);
145 int64_t time_to_empty(int64_t default_value = 0);
146 int64_t time_to_full(int64_t default_value = 0);
147 uint32_t type(uint32_t default_value = UPOWER_DEVICE_TYPE_UNKNOWN);
148
149 private:
150 bool connected_ = false;
151 dbus::Property<bool> is_present_;
152 dbus::Property<double> percentage_;
153 dbus::Property<uint32_t> state_;
154 dbus::Property<int64_t> time_to_empty_;
155 dbus::Property<int64_t> time_to_full_;
156 dbus::Property<uint32_t> type_;
157
158 DISALLOW_COPY_AND_ASSIGN(BatteryProperties);
159 };
160
161 BatteryProperties::BatteryProperties(dbus::ObjectProxy* object_proxy,
162 const PropertyChangedCallback callback)
163 : dbus::PropertySet(object_proxy, kUPowerDeviceInterfaceName, callback) {
164 RegisterProperty(kUPowerDevicePropertyIsPresent, &is_present_);
165 RegisterProperty(kUPowerDevicePropertyPercentage, &percentage_);
166 RegisterProperty(kUPowerDevicePropertyState, &state_);
167 RegisterProperty(kUPowerDevicePropertyTimeToEmpty, &time_to_empty_);
168 RegisterProperty(kUPowerDevicePropertyTimeToFull, &time_to_full_);
169 RegisterProperty(kUPowerDevicePropertyType, &type_);
170 }
171
172 BatteryProperties::~BatteryProperties() {}
173
174 void BatteryProperties::ConnectSignals() {
175 if (!connected_) {
176 connected_ = true;
177 dbus::PropertySet::ConnectSignals();
178 }
179 }
180
181 void BatteryProperties::Invalidate() {
182 is_present_.set_valid(false);
183 percentage_.set_valid(false);
184 state_.set_valid(false);
185 time_to_empty_.set_valid(false);
186 time_to_full_.set_valid(false);
187 type_.set_valid(false);
188 }
189
190 bool BatteryProperties::is_present(bool default_value) {
191 return (is_present_.is_valid() || is_present_.GetAndBlock())
192 ? is_present_.value()
193 : default_value;
194 }
195
196 double BatteryProperties::percentage(double default_value) {
197 return (percentage_.is_valid() || percentage_.GetAndBlock())
198 ? percentage_.value()
199 : default_value;
200 }
201
202 uint32_t BatteryProperties::state(uint32_t default_value) {
203 return (state_.is_valid() || state_.GetAndBlock()) ? state_.value()
204 : default_value;
205 }
206
207 int64_t BatteryProperties::time_to_empty(int64_t default_value) {
208 return (time_to_empty_.is_valid() || time_to_empty_.GetAndBlock())
209 ? time_to_empty_.value()
210 : default_value;
211 }
212
213 int64_t BatteryProperties::time_to_full(int64_t default_value) {
214 return (time_to_full_.is_valid() || time_to_full_.GetAndBlock())
215 ? time_to_full_.value()
216 : default_value;
217 }
218
219 uint32_t BatteryProperties::type(uint32_t default_value) {
220 return (type_.is_valid() || type_.GetAndBlock()) ? type_.value()
221 : default_value;
222 }
223
224 class BatteryObject {
225 public:
226 typedef dbus::PropertySet::PropertyChangedCallback PropertyChangedCallback;
227
228 BatteryObject(dbus::Bus* dbus,
229 const dbus::ObjectPath& device_path,
230 const PropertyChangedCallback& property_changed_callback);
231 ~BatteryObject();
232
233 bool IsValid();
234
235 dbus::ObjectProxy* proxy() { return proxy_; }
236 BatteryProperties* properties() { return properties_.get(); }
237
238 private:
239 dbus::Bus* dbus_; // Owned by the BatteryStatusNotificationThread,
240 dbus::ObjectProxy* proxy_; // Owned by the dbus.
241 std::unique_ptr<BatteryProperties> properties_;
242
243 DISALLOW_COPY_AND_ASSIGN(BatteryObject);
244 };
245
246 BatteryObject::BatteryObject(
247 dbus::Bus* dbus,
248 const dbus::ObjectPath& device_path,
249 const PropertyChangedCallback& property_changed_callback)
250 : dbus_(dbus),
251 proxy_(dbus_->GetObjectProxy(kUPowerServiceName, device_path)),
252 properties_(new BatteryProperties(proxy_, property_changed_callback)) {}
253
254 BatteryObject::~BatteryObject() {
255 properties_.reset(); // before the proxy is deleted.
256 dbus_->RemoveObjectProxy(kUPowerServiceName, proxy_->object_path(),
257 base::Bind(&base::DoNothing));
258 }
259
260 bool BatteryObject::IsValid() {
261 return properties_->is_present() &&
262 properties_->type() == UPOWER_DEVICE_TYPE_BATTERY;
263 }
264
265 BatteryStatus ComputeWebBatteryStatus(BatteryProperties* properties) {
266 BatteryStatus status;
267 uint32_t state = properties->state();
268 status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
269 state != UPOWER_DEVICE_STATE_EMPTY;
270 // Convert percentage to a value between 0 and 1 with 2 digits of precision.
271 // This is to bring it in line with other platforms like Mac and Android where
272 // we report level with 1% granularity. It also serves the purpose of reducing
273 // the possibility of fingerprinting and triggers less level change events on
274 // the blink side.
275 // TODO(timvolodine): consider moving this rounding to the blink side.
276 status.level = round(properties->percentage()) / 100.f;
277
278 switch (state) {
279 case UPOWER_DEVICE_STATE_CHARGING: {
280 int64_t time_to_full = properties->time_to_full();
281 status.charging_time = (time_to_full > 0)
282 ? time_to_full
283 : std::numeric_limits<double>::infinity();
284 break;
285 }
286 case UPOWER_DEVICE_STATE_DISCHARGING: {
287 int64_t time_to_empty = properties->time_to_empty();
288 // Set dischargingTime if it's available. Otherwise leave the default
289 // value which is +infinity.
290 if (time_to_empty > 0)
291 status.discharging_time = time_to_empty;
292 status.charging_time = std::numeric_limits<double>::infinity();
293 break;
294 }
295 case UPOWER_DEVICE_STATE_FULL: {
296 break;
297 }
298 default: { status.charging_time = std::numeric_limits<double>::infinity(); }
299 }
300 return status;
301 }
302
303 } // namespace
304
107 // Class that represents a dedicated thread which communicates with DBus to 305 // Class that represents a dedicated thread which communicates with DBus to
108 // obtain battery information and receives battery change notifications. 306 // obtain battery information and receives battery change notifications.
109 class BatteryStatusNotificationThread : public base::Thread { 307 class BatteryStatusManagerLinux::BatteryStatusNotificationThread
308 : public base::Thread {
110 public: 309 public:
111 BatteryStatusNotificationThread( 310 BatteryStatusNotificationThread(
112 const BatteryStatusService::BatteryUpdateCallback& callback) 311 const BatteryStatusService::BatteryUpdateCallback& callback)
113 : base::Thread(kBatteryNotifierThreadName), 312 : base::Thread(kBatteryNotifierThreadName), callback_(callback) {}
114 callback_(callback),
115 battery_proxy_(NULL) {}
116 313
117 ~BatteryStatusNotificationThread() override { 314 ~BatteryStatusNotificationThread() override {
118 // Make sure to shutdown the dbus connection if it is still open in the very 315 // Make sure to shutdown the dbus connection if it is still open in the very
119 // end. It needs to happen on the BatteryStatusNotificationThread. 316 // end. It needs to happen on the BatteryStatusNotificationThread.
120 message_loop()->PostTask( 317 message_loop()->PostTask(
121 FROM_HERE, 318 FROM_HERE,
122 base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection, 319 base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection,
123 base::Unretained(this))); 320 base::Unretained(this)));
124 321
125 // Drain the message queue of the BatteryStatusNotificationThread and stop. 322 // Drain the message queue of the BatteryStatusNotificationThread and stop.
126 Stop(); 323 Stop();
127 } 324 }
128 325
129 void StartListening() { 326 void StartListening() {
130 DCHECK(OnWatcherThread()); 327 DCHECK(OnWatcherThread());
131 328
132 if (system_bus_.get()) 329 if (upower_)
133 return; 330 return;
134 331
135 InitDBus(); 332 if (!system_bus_)
136 dbus::ObjectProxy* power_proxy = 333 InitDBus();
137 system_bus_->GetObjectProxy(kUPowerServiceName,
138 dbus::ObjectPath(kUPowerPath));
139 std::unique_ptr<PathsVector> device_paths =
140 GetPowerSourcesPaths(power_proxy);
141 int num_batteries = 0;
142 334
143 for (size_t i = 0; i < device_paths->size(); ++i) { 335 upower_.reset(new UPowerObject(system_bus_.get(),
144 const dbus::ObjectPath& device_path = device_paths->at(i); 336 UPowerObject::PropertyChangedCallback()));
145 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy( 337 upower_->proxy()->ConnectToSignal(
146 kUPowerServiceName, device_path); 338 kUPowerServiceName, kUPowerSignalDeviceAdded,
147 std::unique_ptr<base::DictionaryValue> dictionary = 339 base::Bind(&BatteryStatusNotificationThread::DeviceAddedOrChanged,
148 GetPropertiesAsDictionary(device_proxy);
149
150 if (!dictionary)
151 continue;
152
153 bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false);
154 uint32_t type = static_cast<uint32_t>(
155 GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN));
156
157 if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) {
158 system_bus_->RemoveObjectProxy(kUPowerServiceName,
159 device_path,
160 base::Bind(&base::DoNothing));
161 continue;
162 }
163
164 if (battery_proxy_) {
165 // TODO(timvolodine): add support for multiple batteries. Currently we
166 // only collect information from the first battery we encounter
167 // (crbug.com/400780).
168 LOG(WARNING) << "multiple batteries found, "
169 << "using status data of the first battery only.";
170 } else {
171 battery_proxy_ = device_proxy;
172 }
173 num_batteries++;
174 }
175
176 UpdateNumberBatteriesHistogram(num_batteries);
177
178 if (!battery_proxy_) {
179 callback_.Run(BatteryStatus());
180 return;
181 }
182
183 battery_proxy_->ConnectToSignal(
184 kUPowerDeviceName,
185 kUPowerDeviceSignalChanged,
186 base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
187 base::Unretained(this)), 340 base::Unretained(this)),
188 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected, 341 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
189 base::Unretained(this))); 342 base::Unretained(this)));
343 upower_->proxy()->ConnectToSignal(
344 kUPowerServiceName, kUPowerSignalDeviceRemoved,
345 base::Bind(&BatteryStatusNotificationThread::DeviceRemoved,
346 base::Unretained(this)),
347 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
348 base::Unretained(this)));
349
350 if (IsDaemonVersionBelow_0_99()) {
351 // UPower Version 0.99 removed the DeviceChanged signal.
352 upower_->proxy()->ConnectToSignal(
353 kUPowerServiceName, kUPowerSignalDeviceChanged,
354 base::Bind(&BatteryStatusNotificationThread::DeviceAddedOrChanged,
355 base::Unretained(this)),
356 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
357 base::Unretained(this)));
358 }
359
360 FindBatteryDevice();
190 } 361 }
191 362
192 void StopListening() { 363 void StopListening() {
193 DCHECK(OnWatcherThread()); 364 DCHECK(OnWatcherThread());
194 ShutdownDBusConnection(); 365 ShutdownDBusConnection();
195 } 366 }
196 367
368 void SetDBusForTesting(dbus::Bus* bus) { system_bus_ = bus; }
369
197 private: 370 private:
198 bool OnWatcherThread() { 371 bool OnWatcherThread() {
199 return task_runner()->BelongsToCurrentThread(); 372 return task_runner()->BelongsToCurrentThread();
200 } 373 }
201 374
202 void InitDBus() { 375 void InitDBus() {
203 DCHECK(OnWatcherThread()); 376 DCHECK(OnWatcherThread());
204 377
205 dbus::Bus::Options options; 378 dbus::Bus::Options options;
206 options.bus_type = dbus::Bus::SYSTEM; 379 options.bus_type = dbus::Bus::SYSTEM;
207 options.connection_type = dbus::Bus::PRIVATE; 380 options.connection_type = dbus::Bus::PRIVATE;
208 system_bus_ = new dbus::Bus(options); 381 system_bus_ = new dbus::Bus(options);
209 } 382 }
210 383
384 bool IsDaemonVersionBelow_0_99() {
385 base::Version daemon_version = upower_->properties()->daemon_version();
386 return daemon_version.IsValid() &&
387 daemon_version.CompareTo(base::Version("0.99")) < 0;
388 }
389
390 void FindBatteryDevice() {
391 std::unique_ptr<BatteryObject> current = std::move(battery_);
392 auto UseCurrentOrCreateBattery =
393 [&current, this](const dbus::ObjectPath& device_path) {
394 if (current && current->proxy()->object_path() == device_path)
395 return std::move(current);
396 else
397 return CreateBattery(device_path);
398 };
399
400 dbus::ObjectPath display_device_path = upower_->GetDisplayDevice();
401 if (display_device_path.IsValid()) {
402 std::unique_ptr<BatteryObject> battery =
403 UseCurrentOrCreateBattery(display_device_path);
404 if (battery->IsValid())
405 battery_ = std::move(battery);
406 }
407
408 if (!battery_) {
409 int num_batteries = 0;
410 for (const auto& device_path : upower_->EnumerateDevices()) {
411 std::unique_ptr<BatteryObject> battery =
412 UseCurrentOrCreateBattery(device_path);
413
414 if (!battery->IsValid())
415 continue;
416
417 if (battery_) {
418 // TODO(timvolodine): add support for multiple batteries. Currently we
419 // only collect information from the first battery we encounter
420 // (crbug.com/400780).
421 LOG(WARNING) << "multiple batteries found, "
422 << "using status data of the first battery only.";
423 } else {
424 battery_ = std::move(battery);
425 }
426 num_batteries++;
427 }
428
429 UpdateNumberBatteriesHistogram(num_batteries);
430 }
431
432 if (battery_) {
433 battery_->properties()->ConnectSignals();
434 NotifyBatteryStatus();
435 } else {
436 callback_.Run(BatteryStatus());
437 return;
438 }
439
440 if (IsDaemonVersionBelow_0_99()) {
441 // UPower Version 0.99 replaced the Changed signal with the
442 // PropertyChanged signal. For older versions we need to listen
443 // to the Changed signal.
444 battery_->proxy()->ConnectToSignal(
445 kUPowerDeviceInterfaceName, kUPowerDeviceSignalChanged,
446 base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
447 base::Unretained(this)),
448 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
449 base::Unretained(this)));
450 }
451 }
452
211 void ShutdownDBusConnection() { 453 void ShutdownDBusConnection() {
212 DCHECK(OnWatcherThread()); 454 DCHECK(OnWatcherThread());
213 455
214 if (!system_bus_.get()) 456 if (!system_bus_.get())
215 return; 457 return;
216 458
459 battery_.reset(); // before the system_bus_ is shut down.
460 upower_.reset();
461
217 // Shutdown DBus connection later because there may be pending tasks on 462 // Shutdown DBus connection later because there may be pending tasks on
218 // this thread. 463 // this thread.
219 message_loop()->PostTask(FROM_HERE, 464 message_loop()->PostTask(FROM_HERE,
220 base::Bind(&dbus::Bus::ShutdownAndBlock, 465 base::Bind(&dbus::Bus::ShutdownAndBlock,
221 system_bus_)); 466 system_bus_));
222 system_bus_ = NULL; 467 system_bus_ = NULL;
223 battery_proxy_ = NULL;
224 } 468 }
225 469
226 void OnSignalConnected(const std::string& interface_name, 470 void OnSignalConnected(const std::string& interface_name,
227 const std::string& signal_name, 471 const std::string& signal_name,
228 bool success) { 472 bool success) {}
473
474 std::unique_ptr<BatteryObject> CreateBattery(
475 const dbus::ObjectPath& device_path) {
476 std::unique_ptr<BatteryObject> battery(new BatteryObject(
477 system_bus_.get(), device_path,
478 base::Bind(&BatteryStatusNotificationThread::BatteryPropertyChanged,
479 base::Unretained(this))));
480 return battery;
481 }
482
483 void DeviceAddedOrChanged(dbus::Signal* signal /* unused */) {
484 // Re-iterate all devices to see if we need to monitor the added or
485 // changed battery instead of the currently monitored battery.
486 FindBatteryDevice();
487 }
488
489 void DeviceRemoved(dbus::Signal* signal) {
490 if (!battery_)
491 return;
492
493 dbus::MessageReader reader(signal);
494 dbus::ObjectPath removed_device_path;
495 if (reader.PopObjectPath(&removed_device_path) &&
496 battery_->proxy()->object_path() == removed_device_path)
497 FindBatteryDevice();
498 }
499
500 void BatteryPropertyChanged(const std::string& property_name) {
501 NotifyBatteryStatus();
502 }
503
504 void BatteryChanged(dbus::Signal* signal /* unsused */) {
505 DCHECK(battery_);
506 battery_->properties()->Invalidate();
507 NotifyBatteryStatus();
508 }
509
510 void NotifyBatteryStatus() {
229 DCHECK(OnWatcherThread()); 511 DCHECK(OnWatcherThread());
230 512
231 if (interface_name != kUPowerDeviceName || 513 if (!system_bus_.get() || !battery_)
232 signal_name != kUPowerDeviceSignalChanged) { 514 return;
233 return; 515
234 } 516 callback_.Run(ComputeWebBatteryStatus(battery_->properties()));
235
236 if (!system_bus_.get())
237 return;
238
239 if (success) {
240 BatteryChanged(NULL);
241 } else {
242 // Failed to register for "Changed" signal, execute callback with the
243 // default values.
244 callback_.Run(BatteryStatus());
245 }
246 }
247
248 void BatteryChanged(dbus::Signal* signal /* unsused */) {
249 DCHECK(OnWatcherThread());
250
251 if (!system_bus_.get())
252 return;
253
254 std::unique_ptr<base::DictionaryValue> dictionary =
255 GetPropertiesAsDictionary(battery_proxy_);
256 if (dictionary)
257 callback_.Run(ComputeWebBatteryStatus(*dictionary));
258 else
259 callback_.Run(BatteryStatus());
260 } 517 }
261 518
262 BatteryStatusService::BatteryUpdateCallback callback_; 519 BatteryStatusService::BatteryUpdateCallback callback_;
263 scoped_refptr<dbus::Bus> system_bus_; 520 scoped_refptr<dbus::Bus> system_bus_;
264 dbus::ObjectProxy* battery_proxy_; // owned by the bus 521 std::unique_ptr<UPowerObject> upower_;
522 std::unique_ptr<BatteryObject> battery_;
265 523
266 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread); 524 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
267 }; 525 };
268 526
269 // Creates a notification thread and delegates Start/Stop calls to it. 527 BatteryStatusManagerLinux::BatteryStatusManagerLinux(
270 class BatteryStatusManagerLinux : public BatteryStatusManager { 528 const BatteryStatusService::BatteryUpdateCallback& callback)
271 public: 529 : callback_(callback) {}
272 explicit BatteryStatusManagerLinux( 530
273 const BatteryStatusService::BatteryUpdateCallback& callback) 531 BatteryStatusManagerLinux::~BatteryStatusManagerLinux() {}
274 : callback_(callback) {} 532
275 533 bool BatteryStatusManagerLinux::StartListeningBatteryChange() {
276 ~BatteryStatusManagerLinux() override {} 534 if (!StartNotifierThreadIfNecessary())
277 535 return false;
278 private: 536
279 // BatteryStatusManager: 537 notifier_thread_->message_loop()->PostTask(
280 bool StartListeningBatteryChange() override { 538 FROM_HERE, base::Bind(&BatteryStatusNotificationThread::StartListening,
281 if (!StartNotifierThreadIfNecessary()) 539 base::Unretained(notifier_thread_.get())));
282 return false; 540 return true;
283 541 }
284 notifier_thread_->message_loop()->PostTask( 542
285 FROM_HERE, 543 void BatteryStatusManagerLinux::StopListeningBatteryChange() {
286 base::Bind(&BatteryStatusNotificationThread::StartListening, 544 if (!notifier_thread_)
287 base::Unretained(notifier_thread_.get()))); 545 return;
546
547 notifier_thread_->message_loop()->PostTask(
548 FROM_HERE, base::Bind(&BatteryStatusNotificationThread::StopListening,
549 base::Unretained(notifier_thread_.get())));
550 }
551
552 bool BatteryStatusManagerLinux::StartNotifierThreadIfNecessary() {
553 if (notifier_thread_)
288 return true; 554 return true;
289 } 555
290 556 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
291 void StopListeningBatteryChange() override { 557 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
292 if (!notifier_thread_) 558 if (!notifier_thread_->StartWithOptions(thread_options)) {
293 return; 559 notifier_thread_.reset();
294 560 LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
295 notifier_thread_->message_loop()->PostTask( 561 << " thread";
296 FROM_HERE, 562 return false;
297 base::Bind(&BatteryStatusNotificationThread::StopListening, 563 }
298 base::Unretained(notifier_thread_.get()))); 564 return true;
299 } 565 }
300 566
301 // Starts the notifier thread if not already started and returns true on 567 base::Thread* BatteryStatusManagerLinux::GetNotifierThreadForTesting() {
302 // success. 568 return notifier_thread_.get();
303 bool StartNotifierThreadIfNecessary() {
304 if (notifier_thread_)
305 return true;
306
307 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
308 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
309 if (!notifier_thread_->StartWithOptions(thread_options)) {
310 notifier_thread_.reset();
311 LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
312 << " thread";
313 return false;
314 }
315 return true;
316 }
317
318 BatteryStatusService::BatteryUpdateCallback callback_;
319 std::unique_ptr<BatteryStatusNotificationThread> notifier_thread_;
320
321 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
322 };
323
324 } // namespace
325
326 BatteryStatus ComputeWebBatteryStatus(const base::DictionaryValue& dictionary) {
327 BatteryStatus status;
328 if (!dictionary.HasKey("State"))
329 return status;
330
331 uint32_t state = static_cast<uint32_t>(
332 GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN));
333 status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
334 state != UPOWER_DEVICE_STATE_EMPTY;
335 double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100);
336 // Convert percentage to a value between 0 and 1 with 2 digits of precision.
337 // This is to bring it in line with other platforms like Mac and Android where
338 // we report level with 1% granularity. It also serves the purpose of reducing
339 // the possibility of fingerprinting and triggers less level change events on
340 // the blink side.
341 // TODO(timvolodine): consider moving this rounding to the blink side.
342 status.level = round(percentage) / 100.f;
343
344 switch (state) {
345 case UPOWER_DEVICE_STATE_CHARGING : {
346 double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0);
347 status.charging_time =
348 (time_to_full > 0) ? time_to_full
349 : std::numeric_limits<double>::infinity();
350 break;
351 }
352 case UPOWER_DEVICE_STATE_DISCHARGING : {
353 double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0);
354 // Set dischargingTime if it's available. Otherwise leave the default
355 // value which is +infinity.
356 if (time_to_empty > 0)
357 status.discharging_time = time_to_empty;
358 status.charging_time = std::numeric_limits<double>::infinity();
359 break;
360 }
361 case UPOWER_DEVICE_STATE_FULL : {
362 break;
363 }
364 default: {
365 status.charging_time = std::numeric_limits<double>::infinity();
366 }
367 }
368 return status;
369 } 569 }
370 570
371 // static 571 // static
572 std::unique_ptr<BatteryStatusManagerLinux>
573 BatteryStatusManagerLinux::CreateForTesting(
574 const BatteryStatusService::BatteryUpdateCallback& callback,
575 dbus::Bus* bus) {
576 std::unique_ptr<BatteryStatusManagerLinux> manager(
577 new BatteryStatusManagerLinux(callback));
578 if (manager->StartNotifierThreadIfNecessary())
579 manager->notifier_thread_->SetDBusForTesting(bus);
580 else
581 manager.reset();
582 return manager;
583 }
584
585 // static
372 std::unique_ptr<BatteryStatusManager> BatteryStatusManager::Create( 586 std::unique_ptr<BatteryStatusManager> BatteryStatusManager::Create(
373 const BatteryStatusService::BatteryUpdateCallback& callback) { 587 const BatteryStatusService::BatteryUpdateCallback& callback) {
374 return std::unique_ptr<BatteryStatusManager>( 588 return std::unique_ptr<BatteryStatusManager>(
375 new BatteryStatusManagerLinux(callback)); 589 new BatteryStatusManagerLinux(callback));
376 } 590 }
377 591
378 } // namespace device 592 } // namespace device
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698