16e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
26e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
36e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// found in the LICENSE file.
46e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "content/browser/battery_status/battery_status_manager_linux.h"
66e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
76e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/macros.h"
81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/metrics/histogram.h"
96e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/threading/thread.h"
106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "base/values.h"
116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "content/browser/battery_status/battery_status_manager.h"
126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "dbus/bus.h"
146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "dbus/message.h"
156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "dbus/object_path.h"
166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "dbus/object_proxy.h"
176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "dbus/property.h"
186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "dbus/values_util.h"
196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)namespace content {
216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)namespace {
236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const char kUPowerServiceName[] = "org.freedesktop.UPower";
256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const char kUPowerPath[] = "/org/freedesktop/UPower";
276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const char kUPowerDeviceSignalChanged[] = "Changed";
286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const char kUPowerEnumerateDevices[] = "EnumerateDevices";
296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier";
306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// UPowerDeviceType reflects the possible UPower.Device.Type values,
326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// see upower.freedesktop.org/docs/Device.html#Device:Type.
336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)enum UPowerDeviceType {
346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_UNKNOWN = 0,
356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_LINE_POWER = 1,
366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_BATTERY = 2,
376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_UPS = 3,
386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_MONITOR = 4,
396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_MOUSE = 5,
406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_KEYBOARD = 6,
416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_PDA = 7,
426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  UPOWER_DEVICE_TYPE_PHONE = 8,
436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)};
446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)typedef std::vector<dbus::ObjectPath> PathsVector;
466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)double GetPropertyAsDouble(const base::DictionaryValue& dictionary,
486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                           const std::string& property_name,
496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                           double default_value) {
506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  double value = default_value;
516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return dictionary.GetDouble(property_name, &value) ? value : default_value;
526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary,
556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                          const std::string& property_name,
566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                          bool default_value) {
576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  bool value = default_value;
586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return dictionary.GetBoolean(property_name, &value) ? value : default_value;
596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    dbus::ObjectProxy* proxy) {
636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  dbus::MethodCall method_call(dbus::kPropertiesInterface,
646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                               dbus::kPropertiesGetAll);
656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  dbus::MessageWriter builder(&method_call);
666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  builder.AppendString(kUPowerDeviceName);
676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<dbus::Response> response(
696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      proxy->CallMethodAndBlock(&method_call,
706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (response) {
726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    dbus::MessageReader reader(response.get());
736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    base::DictionaryValue* dictionary_value = NULL;
756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (value && value->GetAsDictionary(&dictionary_value)) {
766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      ignore_result(value.release());
776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return scoped_ptr<base::DictionaryValue>(dictionary_value);
786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return scoped_ptr<base::DictionaryValue>();
816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)scoped_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) {
846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<PathsVector> paths(new PathsVector());
856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!proxy)
866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return paths.Pass();
876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices);
896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<dbus::Response> response(
906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      proxy->CallMethodAndBlock(&method_call,
916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (response) {
946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    dbus::MessageReader reader(response.get());
956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    reader.PopArrayOfObjectPaths(paths.get());
966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return paths.Pass();;
986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1001320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccivoid UpdateNumberBatteriesHistogram(int count) {
1011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  UMA_HISTOGRAM_CUSTOM_COUNTS(
1021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      "BatteryStatus.NumberBatteriesLinux", count, 1, 5, 6);
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Class that represents a dedicated thread which communicates with DBus to
1066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// obtain battery information and receives battery change notifications.
1076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)class BatteryStatusNotificationThread : public base::Thread {
1086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public:
1096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  BatteryStatusNotificationThread(
1106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      const BatteryStatusService::BatteryUpdateCallback& callback)
1116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      : base::Thread(kBatteryNotifierThreadName),
1126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        callback_(callback),
1136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        battery_proxy_(NULL) {}
1146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual ~BatteryStatusNotificationThread() {
1166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Make sure to shutdown the dbus connection if it is still open in the very
1196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // end. It needs to happen on the BatteryStatusNotificationThread.
1206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    message_loop()->PostTask(
1216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        FROM_HERE,
1226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection,
1236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   base::Unretained(this)));
1246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Drain the message queue of the BatteryStatusNotificationThread and stop.
1266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Stop();
1276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void StartListening() {
1306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(OnWatcherThread());
1316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1321320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (system_bus_.get())
1336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return;
1346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    InitDBus();
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    dbus::ObjectProxy* power_proxy =
1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        system_bus_->GetObjectProxy(kUPowerServiceName,
1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                    dbus::ObjectPath(kUPowerPath));
1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    scoped_ptr<PathsVector> device_paths = GetPowerSourcesPaths(power_proxy);
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    int num_batteries = 0;
1416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    for (size_t i = 0; i < device_paths->size(); ++i) {
1436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      const dbus::ObjectPath& device_path = device_paths->at(i);
1446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
1456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          kUPowerServiceName, device_path);
1466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      scoped_ptr<base::DictionaryValue> dictionary =
1476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          GetPropertiesAsDictionary(device_proxy);
1486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      if (!dictionary)
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        continue;
1516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false);
1536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      uint32 type = static_cast<uint32>(
1546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN));
1556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) {
1576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        system_bus_->RemoveObjectProxy(kUPowerServiceName,
1586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                       device_path,
1596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                       base::Bind(&base::DoNothing));
1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        continue;
1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      }
1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      if (battery_proxy_) {
1646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        // TODO(timvolodine): add support for multiple batteries. Currently we
1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        // only collect information from the first battery we encounter
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        // (crbug.com/400780).
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        LOG(WARNING) << "multiple batteries found, "
1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                     << "using status data of the first battery only.";
1696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      } else {
1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        battery_proxy_ = device_proxy;
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      }
1721320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      num_batteries++;
1736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
1746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1751320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    UpdateNumberBatteriesHistogram(num_batteries);
1761320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
1776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (!battery_proxy_) {
1786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      callback_.Run(blink::WebBatteryStatus());
1796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return;
1806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
1816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    battery_proxy_->ConnectToSignal(
1836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        kUPowerDeviceName,
1846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        kUPowerDeviceSignalChanged,
1856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
1866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   base::Unretained(this)),
1876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
1886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   base::Unretained(this)));
1896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void StopListening() {
1926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(OnWatcherThread());
1936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    ShutdownDBusConnection();
1946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
1956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private:
1976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  bool OnWatcherThread() {
1986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return task_runner()->BelongsToCurrentThread();
1996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void InitDBus() {
2026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(OnWatcherThread());
2036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    dbus::Bus::Options options;
2056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    options.bus_type = dbus::Bus::SYSTEM;
2066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    options.connection_type = dbus::Bus::PRIVATE;
2076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    system_bus_ = new dbus::Bus(options);
2086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void ShutdownDBusConnection() {
2116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(OnWatcherThread());
2126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!system_bus_.get())
2146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return;
2156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // Shutdown DBus connection later because there may be pending tasks on
2176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    // this thread.
2186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    message_loop()->PostTask(FROM_HERE,
2196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                             base::Bind(&dbus::Bus::ShutdownAndBlock,
2206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                        system_bus_));
2216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    system_bus_ = NULL;
2226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    battery_proxy_ = NULL;
2236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void OnSignalConnected(const std::string& interface_name,
2266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                         const std::string& signal_name,
2276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                         bool success) {
2286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(OnWatcherThread());
2296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (interface_name != kUPowerDeviceName ||
2316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        signal_name != kUPowerDeviceSignalChanged) {
2326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return;
2336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
2346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2351320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!system_bus_.get())
2366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return;
2376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (success) {
2396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      BatteryChanged(NULL);
2406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    } else {
2416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      // Failed to register for "Changed" signal, execute callback with the
2426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      // default values.
2436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      callback_.Run(blink::WebBatteryStatus());
2446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
2456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void BatteryChanged(dbus::Signal* signal /* unsused */) {
2486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(OnWatcherThread());
2496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2501320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (!system_bus_.get())
2516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return;
2526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    scoped_ptr<base::DictionaryValue> dictionary =
2546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        GetPropertiesAsDictionary(battery_proxy_);
2556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (dictionary)
2566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      callback_.Run(ComputeWebBatteryStatus(*dictionary));
2576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    else
2586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      callback_.Run(blink::WebBatteryStatus());
2596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  BatteryStatusService::BatteryUpdateCallback callback_;
2626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_refptr<dbus::Bus> system_bus_;
2636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  dbus::ObjectProxy* battery_proxy_;  // owned by the bus
2646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
2666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)};
2676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// Runs on IO thread and creates a notification thread and delegates Start/Stop
2696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// calls to it.
2706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)class BatteryStatusManagerLinux : public BatteryStatusManager {
2716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) public:
2726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  explicit BatteryStatusManagerLinux(
2736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      const BatteryStatusService::BatteryUpdateCallback& callback)
2746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      : callback_(callback) {}
2756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual ~BatteryStatusManagerLinux() {}
2776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles) private:
2796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // BatteryStatusManager:
2806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual bool StartListeningBatteryChange() OVERRIDE {
2816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
2826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (!StartNotifierThreadIfNecessary())
2846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return false;
2856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    notifier_thread_->message_loop()->PostTask(
2876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        FROM_HERE,
2886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        base::Bind(&BatteryStatusNotificationThread::StartListening,
2896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   base::Unretained(notifier_thread_.get())));
2906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return true;
2916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
2926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  virtual void StopListeningBatteryChange() OVERRIDE {
2946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
2956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2966e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (!notifier_thread_)
2976e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return;
2986e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
2996e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    notifier_thread_->message_loop()->PostTask(
3006e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        FROM_HERE,
3016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        base::Bind(&BatteryStatusNotificationThread::StopListening,
3026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   base::Unretained(notifier_thread_.get())));
3036e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
3046e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3056e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // Starts the notifier thread if not already started and returns true on
3066e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // success.
3076e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  bool StartNotifierThreadIfNecessary() {
3086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (notifier_thread_)
3096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return true;
3106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
3126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
3136e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    if (!notifier_thread_->StartWithOptions(thread_options)) {
3146e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      notifier_thread_.reset();
3156e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
3166e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                 << " thread";
3176e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      return false;
3186e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
3196e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return true;
3206e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
3216e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3226e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  BatteryStatusService::BatteryUpdateCallback callback_;
3236e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  scoped_ptr<BatteryStatusNotificationThread> notifier_thread_;
3246e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3256e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
3266e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)};
3276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3286e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}  // namespace
3296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)blink::WebBatteryStatus ComputeWebBatteryStatus(
3316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const base::DictionaryValue& dictionary) {
3326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  blink::WebBatteryStatus status;
3336e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (!dictionary.HasKey("State"))
3346e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return status;
3356e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  uint32 state = static_cast<uint32>(
3376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN));
3386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
3396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                    state != UPOWER_DEVICE_STATE_EMPTY;
3406e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100);
3416e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // Convert percentage to a value between 0 and 1 with 2 digits of precision.
3426e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // This is to bring it in line with other platforms like Mac and Android where
3436e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // we report level with 1% granularity. It also serves the purpose of reducing
3446e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // the possibility of fingerprinting and triggers less level change events on
3456e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // the blink side.
3466e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  // TODO(timvolodine): consider moving this rounding to the blink side.
3476e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  status.level = round(percentage) / 100.f;
3486e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3496e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  switch (state) {
3506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case UPOWER_DEVICE_STATE_CHARGING : {
3516e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0);
3526e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      status.chargingTime =
3536e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)          (time_to_full > 0) ? time_to_full
3546e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                             : std::numeric_limits<double>::infinity();
3556e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      break;
3566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
3576e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case UPOWER_DEVICE_STATE_DISCHARGING : {
3586e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0);
3596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      // Set dischargingTime if it's available. Otherwise leave the default
3606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      // value which is +infinity.
3616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      if (time_to_empty > 0)
3626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)        status.dischargingTime = time_to_empty;
3636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      status.chargingTime = std::numeric_limits<double>::infinity();
3646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      break;
3656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
3666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case UPOWER_DEVICE_STATE_FULL : {
3676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      break;
3686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
3696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    default: {
3706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      status.chargingTime = std::numeric_limits<double>::infinity();
3716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
3726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  }
3736e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return status;
3746e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
3756e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3766e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)// static
3776e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
3786e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const BatteryStatusService::BatteryUpdateCallback& callback) {
3796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return scoped_ptr<BatteryStatusManager>(
3806e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      new BatteryStatusManagerLinux(callback));
3816e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
3826e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
3836e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}  // namespace content
384