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

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, 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 <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) {
markuso 2016/08/19 12:02:51 I noticed this is the wrong interface-name. We nee
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,
markuso 2016/08/19 12:02:51 There is no need to connect to the "DeviceChanged"
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 // Move the currently watched battery_ device to a stack-local variable such
392 // that we can enumerate all devices (once more):
393 // first testing the display device, then testing all devices from
394 // EnumerateDevices. We will monitor the first battery device we find.
395 // - That may be the same device we did monitor on entering this method;
396 // then we'll use the same BatteryObject instance, that was moved to
397 // current - see UseCurrentOrCreateBattery().
398 // - Or it may be a new device; then the previously monitored BatteryObject
399 // instance (if any) is released on leaving this function.
400 // - Or we may not find a battery device; then on leaving this function
401 // battery_ will be nullptr and the previously monitored BatteryObject
402 // instance (if any) is no longer a battery and will be released.
403 std::unique_ptr<BatteryObject> current = std::move(battery_);
404 auto UseCurrentOrCreateBattery =
405 [&current, this](const dbus::ObjectPath& device_path) {
406 if (current && current->proxy()->object_path() == device_path)
407 return std::move(current);
408 else
409 return CreateBattery(device_path);
410 };
411
412 dbus::ObjectPath display_device_path = upower_->GetDisplayDevice();
413 if (display_device_path.IsValid()) {
414 std::unique_ptr<BatteryObject> battery =
415 UseCurrentOrCreateBattery(display_device_path);
416 if (battery->IsValid())
417 battery_ = std::move(battery);
418 }
419
420 if (!battery_) {
421 int num_batteries = 0;
422 for (const auto& device_path : upower_->EnumerateDevices()) {
423 std::unique_ptr<BatteryObject> battery =
424 UseCurrentOrCreateBattery(device_path);
425
426 if (!battery->IsValid())
427 continue;
428
429 if (battery_) {
430 // TODO(timvolodine): add support for multiple batteries. Currently we
431 // only collect information from the first battery we encounter
432 // (crbug.com/400780).
433 LOG(WARNING) << "multiple batteries found, "
434 << "using status data of the first battery only.";
435 } else {
436 battery_ = std::move(battery);
437 }
438 num_batteries++;
439 }
440
441 UpdateNumberBatteriesHistogram(num_batteries);
442 }
443
444 if (battery_) {
445 battery_->properties()->ConnectSignals();
446 NotifyBatteryStatus();
447 } else {
448 callback_.Run(BatteryStatus());
449 return;
450 }
451
452 if (IsDaemonVersionBelow_0_99()) {
453 // UPower Version 0.99 replaced the Changed signal with the
454 // PropertyChanged signal. For older versions we need to listen
455 // to the Changed signal.
456 battery_->proxy()->ConnectToSignal(
457 kUPowerDeviceInterfaceName, kUPowerDeviceSignalChanged,
458 base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
459 base::Unretained(this)),
460 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
461 base::Unretained(this)));
462 }
463 }
464
211 void ShutdownDBusConnection() { 465 void ShutdownDBusConnection() {
212 DCHECK(OnWatcherThread()); 466 DCHECK(OnWatcherThread());
213 467
214 if (!system_bus_.get()) 468 if (!system_bus_.get())
215 return; 469 return;
216 470
471 battery_.reset(); // before the system_bus_ is shut down.
472 upower_.reset();
473
217 // Shutdown DBus connection later because there may be pending tasks on 474 // Shutdown DBus connection later because there may be pending tasks on
218 // this thread. 475 // this thread.
219 message_loop()->PostTask(FROM_HERE, 476 message_loop()->PostTask(FROM_HERE,
220 base::Bind(&dbus::Bus::ShutdownAndBlock, 477 base::Bind(&dbus::Bus::ShutdownAndBlock,
221 system_bus_)); 478 system_bus_));
222 system_bus_ = NULL; 479 system_bus_ = NULL;
223 battery_proxy_ = NULL;
224 } 480 }
225 481
226 void OnSignalConnected(const std::string& interface_name, 482 void OnSignalConnected(const std::string& interface_name,
227 const std::string& signal_name, 483 const std::string& signal_name,
228 bool success) { 484 bool success) {}
485
486 std::unique_ptr<BatteryObject> CreateBattery(
487 const dbus::ObjectPath& device_path) {
488 std::unique_ptr<BatteryObject> battery(new BatteryObject(
489 system_bus_.get(), device_path,
490 base::Bind(&BatteryStatusNotificationThread::BatteryPropertyChanged,
491 base::Unretained(this))));
492 return battery;
493 }
494
495 void DeviceAddedOrChanged(dbus::Signal* signal /* unused */) {
496 // Re-iterate all devices to see if we need to monitor the added or
497 // changed battery instead of the currently monitored battery.
498 FindBatteryDevice();
499 }
500
501 void DeviceRemoved(dbus::Signal* signal) {
502 if (!battery_)
503 return;
504
505 dbus::MessageReader reader(signal);
506 dbus::ObjectPath removed_device_path;
507 if (reader.PopObjectPath(&removed_device_path) &&
markuso 2016/08/19 12:02:51 UPower specifies that the signal 'DeviceRemoved' h
508 battery_->proxy()->object_path() == removed_device_path)
509 FindBatteryDevice();
510 }
511
512 void BatteryPropertyChanged(const std::string& property_name) {
513 NotifyBatteryStatus();
514 }
515
516 void BatteryChanged(dbus::Signal* signal /* unsused */) {
517 DCHECK(battery_);
518 battery_->properties()->Invalidate();
markuso 2016/08/19 12:02:51 If the system uses a UPower daemon older than vers
519 NotifyBatteryStatus();
520 }
521
522 void NotifyBatteryStatus() {
229 DCHECK(OnWatcherThread()); 523 DCHECK(OnWatcherThread());
230 524
231 if (interface_name != kUPowerDeviceName || 525 if (!system_bus_.get() || !battery_)
232 signal_name != kUPowerDeviceSignalChanged) { 526 return;
233 return; 527
234 } 528 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 } 529 }
261 530
262 BatteryStatusService::BatteryUpdateCallback callback_; 531 BatteryStatusService::BatteryUpdateCallback callback_;
263 scoped_refptr<dbus::Bus> system_bus_; 532 scoped_refptr<dbus::Bus> system_bus_;
264 dbus::ObjectProxy* battery_proxy_; // owned by the bus 533 std::unique_ptr<UPowerObject> upower_;
534 std::unique_ptr<BatteryObject> battery_;
265 535
266 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread); 536 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
267 }; 537 };
268 538
269 // Creates a notification thread and delegates Start/Stop calls to it. 539 BatteryStatusManagerLinux::BatteryStatusManagerLinux(
270 class BatteryStatusManagerLinux : public BatteryStatusManager { 540 const BatteryStatusService::BatteryUpdateCallback& callback)
271 public: 541 : callback_(callback) {}
272 explicit BatteryStatusManagerLinux( 542
273 const BatteryStatusService::BatteryUpdateCallback& callback) 543 BatteryStatusManagerLinux::~BatteryStatusManagerLinux() {}
274 : callback_(callback) {} 544
275 545 bool BatteryStatusManagerLinux::StartListeningBatteryChange() {
276 ~BatteryStatusManagerLinux() override {} 546 if (!StartNotifierThreadIfNecessary())
277 547 return false;
278 private: 548
279 // BatteryStatusManager: 549 notifier_thread_->message_loop()->PostTask(
280 bool StartListeningBatteryChange() override { 550 FROM_HERE, base::Bind(&BatteryStatusNotificationThread::StartListening,
281 if (!StartNotifierThreadIfNecessary()) 551 base::Unretained(notifier_thread_.get())));
282 return false; 552 return true;
283 553 }
284 notifier_thread_->message_loop()->PostTask( 554
285 FROM_HERE, 555 void BatteryStatusManagerLinux::StopListeningBatteryChange() {
286 base::Bind(&BatteryStatusNotificationThread::StartListening, 556 if (!notifier_thread_)
287 base::Unretained(notifier_thread_.get()))); 557 return;
558
559 notifier_thread_->message_loop()->PostTask(
560 FROM_HERE, base::Bind(&BatteryStatusNotificationThread::StopListening,
561 base::Unretained(notifier_thread_.get())));
562 }
563
564 bool BatteryStatusManagerLinux::StartNotifierThreadIfNecessary() {
565 if (notifier_thread_)
288 return true; 566 return true;
289 } 567
290 568 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
291 void StopListeningBatteryChange() override { 569 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
292 if (!notifier_thread_) 570 if (!notifier_thread_->StartWithOptions(thread_options)) {
293 return; 571 notifier_thread_.reset();
294 572 LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
295 notifier_thread_->message_loop()->PostTask( 573 << " thread";
296 FROM_HERE, 574 return false;
297 base::Bind(&BatteryStatusNotificationThread::StopListening, 575 }
298 base::Unretained(notifier_thread_.get()))); 576 return true;
299 } 577 }
300 578
301 // Starts the notifier thread if not already started and returns true on 579 base::Thread* BatteryStatusManagerLinux::GetNotifierThreadForTesting() {
302 // success. 580 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 } 581 }
370 582
371 // static 583 // static
584 std::unique_ptr<BatteryStatusManagerLinux>
585 BatteryStatusManagerLinux::CreateForTesting(
586 const BatteryStatusService::BatteryUpdateCallback& callback,
587 dbus::Bus* bus) {
588 std::unique_ptr<BatteryStatusManagerLinux> manager(
589 new BatteryStatusManagerLinux(callback));
590 if (manager->StartNotifierThreadIfNecessary())
591 manager->notifier_thread_->SetDBusForTesting(bus);
592 else
593 manager.reset();
594 return manager;
595 }
596
597 // static
372 std::unique_ptr<BatteryStatusManager> BatteryStatusManager::Create( 598 std::unique_ptr<BatteryStatusManager> BatteryStatusManager::Create(
373 const BatteryStatusService::BatteryUpdateCallback& callback) { 599 const BatteryStatusService::BatteryUpdateCallback& callback) {
374 return std::unique_ptr<BatteryStatusManager>( 600 return std::unique_ptr<BatteryStatusManager>(
375 new BatteryStatusManagerLinux(callback)); 601 new BatteryStatusManagerLinux(callback));
376 } 602 }
377 603
378 } // namespace device 604 } // 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