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