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