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

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

Powered by Google App Engine
This is Rietveld 408576698