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

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

Issue 676953005: Revert of Replace Chrome IPC with Mojo IPC for querying BatteryStatus service (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "device/battery/battery_status_manager_linux.h"
6
7 #include "base/macros.h"
8 #include "base/metrics/histogram.h"
9 #include "base/threading/thread.h"
10 #include "base/values.h"
11 #include "dbus/bus.h"
12 #include "dbus/message.h"
13 #include "dbus/object_path.h"
14 #include "dbus/object_proxy.h"
15 #include "dbus/property.h"
16 #include "dbus/values_util.h"
17 #include "device/battery/battery_status_manager.h"
18
19 namespace device {
20
21 namespace {
22
23 const char kUPowerServiceName[] = "org.freedesktop.UPower";
24 const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
25 const char kUPowerPath[] = "/org/freedesktop/UPower";
26 const char kUPowerDeviceSignalChanged[] = "Changed";
27 const char kUPowerEnumerateDevices[] = "EnumerateDevices";
28 const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier";
29
30 // UPowerDeviceType reflects the possible UPower.Device.Type values,
31 // see upower.freedesktop.org/docs/Device.html#Device:Type.
32 enum UPowerDeviceType {
33 UPOWER_DEVICE_TYPE_UNKNOWN = 0,
34 UPOWER_DEVICE_TYPE_LINE_POWER = 1,
35 UPOWER_DEVICE_TYPE_BATTERY = 2,
36 UPOWER_DEVICE_TYPE_UPS = 3,
37 UPOWER_DEVICE_TYPE_MONITOR = 4,
38 UPOWER_DEVICE_TYPE_MOUSE = 5,
39 UPOWER_DEVICE_TYPE_KEYBOARD = 6,
40 UPOWER_DEVICE_TYPE_PDA = 7,
41 UPOWER_DEVICE_TYPE_PHONE = 8,
42 };
43
44 typedef std::vector<dbus::ObjectPath> PathsVector;
45
46 double GetPropertyAsDouble(const base::DictionaryValue& dictionary,
47 const std::string& property_name,
48 double default_value) {
49 double value = default_value;
50 return dictionary.GetDouble(property_name, &value) ? value : default_value;
51 }
52
53 bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary,
54 const std::string& property_name,
55 bool default_value) {
56 bool value = default_value;
57 return dictionary.GetBoolean(property_name, &value) ? value : default_value;
58 }
59
60 scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
61 dbus::ObjectProxy* proxy) {
62 dbus::MethodCall method_call(dbus::kPropertiesInterface,
63 dbus::kPropertiesGetAll);
64 dbus::MessageWriter builder(&method_call);
65 builder.AppendString(kUPowerDeviceName);
66
67 scoped_ptr<dbus::Response> response(
68 proxy->CallMethodAndBlock(&method_call,
69 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
70 if (response) {
71 dbus::MessageReader reader(response.get());
72 scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
73 base::DictionaryValue* dictionary_value = NULL;
74 if (value && value->GetAsDictionary(&dictionary_value)) {
75 ignore_result(value.release());
76 return scoped_ptr<base::DictionaryValue>(dictionary_value);
77 }
78 }
79 return scoped_ptr<base::DictionaryValue>();
80 }
81
82 scoped_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) {
83 scoped_ptr<PathsVector> paths(new PathsVector());
84 if (!proxy)
85 return paths.Pass();
86
87 dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices);
88 scoped_ptr<dbus::Response> response(
89 proxy->CallMethodAndBlock(&method_call,
90 dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
91
92 if (response) {
93 dbus::MessageReader reader(response.get());
94 reader.PopArrayOfObjectPaths(paths.get());
95 }
96 return paths.Pass();;
97 }
98
99 void UpdateNumberBatteriesHistogram(int count) {
100 UMA_HISTOGRAM_CUSTOM_COUNTS(
101 "BatteryStatus.NumberBatteriesLinux", count, 1, 5, 6);
102 }
103
104 // Class that represents a dedicated thread which communicates with DBus to
105 // obtain battery information and receives battery change notifications.
106 class BatteryStatusNotificationThread : public base::Thread {
107 public:
108 BatteryStatusNotificationThread(
109 const BatteryStatusService::BatteryUpdateCallback& callback)
110 : base::Thread(kBatteryNotifierThreadName),
111 callback_(callback),
112 battery_proxy_(NULL) {}
113
114 virtual ~BatteryStatusNotificationThread() {
115 // Make sure to shutdown the dbus connection if it is still open in the very
116 // end. It needs to happen on the BatteryStatusNotificationThread.
117 message_loop()->PostTask(
118 FROM_HERE,
119 base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection,
120 base::Unretained(this)));
121
122 // Drain the message queue of the BatteryStatusNotificationThread and stop.
123 Stop();
124 }
125
126 void StartListening() {
127 DCHECK(OnWatcherThread());
128
129 if (system_bus_.get())
130 return;
131
132 InitDBus();
133 dbus::ObjectProxy* power_proxy =
134 system_bus_->GetObjectProxy(kUPowerServiceName,
135 dbus::ObjectPath(kUPowerPath));
136 scoped_ptr<PathsVector> device_paths = GetPowerSourcesPaths(power_proxy);
137 int num_batteries = 0;
138
139 for (size_t i = 0; i < device_paths->size(); ++i) {
140 const dbus::ObjectPath& device_path = device_paths->at(i);
141 dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
142 kUPowerServiceName, device_path);
143 scoped_ptr<base::DictionaryValue> dictionary =
144 GetPropertiesAsDictionary(device_proxy);
145
146 if (!dictionary)
147 continue;
148
149 bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false);
150 uint32 type = static_cast<uint32>(
151 GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN));
152
153 if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) {
154 system_bus_->RemoveObjectProxy(kUPowerServiceName,
155 device_path,
156 base::Bind(&base::DoNothing));
157 continue;
158 }
159
160 if (battery_proxy_) {
161 // TODO(timvolodine): add support for multiple batteries. Currently we
162 // only collect information from the first battery we encounter
163 // (crbug.com/400780).
164 LOG(WARNING) << "multiple batteries found, "
165 << "using status data of the first battery only.";
166 } else {
167 battery_proxy_ = device_proxy;
168 }
169 num_batteries++;
170 }
171
172 UpdateNumberBatteriesHistogram(num_batteries);
173
174 if (!battery_proxy_) {
175 callback_.Run(BatteryStatus());
176 return;
177 }
178
179 battery_proxy_->ConnectToSignal(
180 kUPowerDeviceName,
181 kUPowerDeviceSignalChanged,
182 base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
183 base::Unretained(this)),
184 base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
185 base::Unretained(this)));
186 }
187
188 void StopListening() {
189 DCHECK(OnWatcherThread());
190 ShutdownDBusConnection();
191 }
192
193 private:
194 bool OnWatcherThread() {
195 return task_runner()->BelongsToCurrentThread();
196 }
197
198 void InitDBus() {
199 DCHECK(OnWatcherThread());
200
201 dbus::Bus::Options options;
202 options.bus_type = dbus::Bus::SYSTEM;
203 options.connection_type = dbus::Bus::PRIVATE;
204 system_bus_ = new dbus::Bus(options);
205 }
206
207 void ShutdownDBusConnection() {
208 DCHECK(OnWatcherThread());
209
210 if (!system_bus_.get())
211 return;
212
213 // Shutdown DBus connection later because there may be pending tasks on
214 // this thread.
215 message_loop()->PostTask(FROM_HERE,
216 base::Bind(&dbus::Bus::ShutdownAndBlock,
217 system_bus_));
218 system_bus_ = NULL;
219 battery_proxy_ = NULL;
220 }
221
222 void OnSignalConnected(const std::string& interface_name,
223 const std::string& signal_name,
224 bool success) {
225 DCHECK(OnWatcherThread());
226
227 if (interface_name != kUPowerDeviceName ||
228 signal_name != kUPowerDeviceSignalChanged) {
229 return;
230 }
231
232 if (!system_bus_.get())
233 return;
234
235 if (success) {
236 BatteryChanged(NULL);
237 } else {
238 // Failed to register for "Changed" signal, execute callback with the
239 // default values.
240 callback_.Run(BatteryStatus());
241 }
242 }
243
244 void BatteryChanged(dbus::Signal* signal /* unsused */) {
245 DCHECK(OnWatcherThread());
246
247 if (!system_bus_.get())
248 return;
249
250 scoped_ptr<base::DictionaryValue> dictionary =
251 GetPropertiesAsDictionary(battery_proxy_);
252 if (dictionary)
253 callback_.Run(ComputeWebBatteryStatus(*dictionary));
254 else
255 callback_.Run(BatteryStatus());
256 }
257
258 BatteryStatusService::BatteryUpdateCallback callback_;
259 scoped_refptr<dbus::Bus> system_bus_;
260 dbus::ObjectProxy* battery_proxy_; // owned by the bus
261
262 DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
263 };
264
265 // Creates a notification thread and delegates Start/Stop calls to it.
266 class BatteryStatusManagerLinux : public BatteryStatusManager {
267 public:
268 explicit BatteryStatusManagerLinux(
269 const BatteryStatusService::BatteryUpdateCallback& callback)
270 : callback_(callback) {}
271
272 virtual ~BatteryStatusManagerLinux() {}
273
274 private:
275 // BatteryStatusManager:
276 virtual bool StartListeningBatteryChange() override {
277 return false;
278
279 notifier_thread_->message_loop()->PostTask(
280 FROM_HERE,
281 base::Bind(&BatteryStatusNotificationThread::StartListening,
282 base::Unretained(notifier_thread_.get())));
283 return true;
284 }
285
286 virtual void StopListeningBatteryChange() override {
287 if (!notifier_thread_)
288 return;
289
290 notifier_thread_->message_loop()->PostTask(
291 FROM_HERE,
292 base::Bind(&BatteryStatusNotificationThread::StopListening,
293 base::Unretained(notifier_thread_.get())));
294 }
295
296 // Starts the notifier thread if not already started and returns true on
297 // success.
298 bool StartNotifierThreadIfNecessary() {
299 if (notifier_thread_)
300 return true;
301
302 base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
303 notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
304 if (!notifier_thread_->StartWithOptions(thread_options)) {
305 notifier_thread_.reset();
306 LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
307 << " thread";
308 return false;
309 }
310 return true;
311 }
312
313 BatteryStatusService::BatteryUpdateCallback callback_;
314 scoped_ptr<BatteryStatusNotificationThread> notifier_thread_;
315
316 DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
317 };
318
319 } // namespace
320
321 BatteryStatus ComputeWebBatteryStatus(const base::DictionaryValue& dictionary) {
322 BatteryStatus status;
323 if (!dictionary.HasKey("State"))
324 return status;
325
326 uint32 state = static_cast<uint32>(
327 GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN));
328 status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
329 state != UPOWER_DEVICE_STATE_EMPTY;
330 double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100);
331 // Convert percentage to a value between 0 and 1 with 2 digits of precision.
332 // This is to bring it in line with other platforms like Mac and Android where
333 // we report level with 1% granularity. It also serves the purpose of reducing
334 // the possibility of fingerprinting and triggers less level change events on
335 // the blink side.
336 // TODO(timvolodine): consider moving this rounding to the blink side.
337 status.level = round(percentage) / 100.f;
338
339 switch (state) {
340 case UPOWER_DEVICE_STATE_CHARGING : {
341 double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0);
342 status.charging_time =
343 (time_to_full > 0) ? time_to_full
344 : std::numeric_limits<double>::infinity();
345 break;
346 }
347 case UPOWER_DEVICE_STATE_DISCHARGING : {
348 double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0);
349 // Set dischargingTime if it's available. Otherwise leave the default
350 // value which is +infinity.
351 if (time_to_empty > 0)
352 status.discharging_time = time_to_empty;
353 status.charging_time = std::numeric_limits<double>::infinity();
354 break;
355 }
356 case UPOWER_DEVICE_STATE_FULL : {
357 break;
358 }
359 default: {
360 status.charging_time = std::numeric_limits<double>::infinity();
361 }
362 }
363 return status;
364 }
365
366 // static
367 scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
368 const BatteryStatusService::BatteryUpdateCallback& callback) {
369 return scoped_ptr<BatteryStatusManager>(
370 new BatteryStatusManagerLinux(callback));
371 }
372
373 } // namespace device
OLDNEW
« no previous file with comments | « device/battery/battery_status_manager_linux.h ('k') | device/battery/battery_status_manager_linux_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698