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

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

Powered by Google App Engine
This is Rietveld 408576698