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

Unified Diff: content/browser/battery_status/battery_status_manager_linux.cc

Issue 436683002: Battery Status API: implementation for Linux. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix chromeOS build Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: content/browser/battery_status/battery_status_manager_linux.cc
diff --git a/content/browser/battery_status/battery_status_manager_linux.cc b/content/browser/battery_status/battery_status_manager_linux.cc
new file mode 100644
index 0000000000000000000000000000000000000000..e6abf88ed4eb11b4f53ee7b1e9d771e87caf98d5
--- /dev/null
+++ b/content/browser/battery_status/battery_status_manager_linux.cc
@@ -0,0 +1,374 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "content/browser/battery_status/battery_status_manager_linux.h"
+
+#include "base/macros.h"
+#include "base/threading/thread.h"
+#include "base/values.h"
+#include "content/browser/battery_status/battery_status_manager.h"
+#include "content/public/browser/browser_thread.h"
+#include "dbus/bus.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "dbus/object_proxy.h"
+#include "dbus/property.h"
+#include "dbus/values_util.h"
+
+namespace content {
+
+namespace {
+
+const char kUPowerServiceName[] = "org.freedesktop.UPower";
+const char kUPowerDeviceName[] = "org.freedesktop.UPower.Device";
+const char kUPowerPath[] = "/org/freedesktop/UPower";
+const char kUPowerDeviceSignalChanged[] = "Changed";
+const char kUPowerEnumerateDevices[] = "EnumerateDevices";
+const char kBatteryNotifierThreadName[] = "BatteryStatusNotifier";
+
+// UPowerDeviceType reflects the possible UPower.Device.Type values,
+// see upower.freedesktop.org/docs/Device.html#Device:Type.
+enum UPowerDeviceType {
+ UPOWER_DEVICE_TYPE_UNKNOWN = 0,
+ UPOWER_DEVICE_TYPE_LINE_POWER = 1,
+ UPOWER_DEVICE_TYPE_BATTERY = 2,
+ UPOWER_DEVICE_TYPE_UPS = 3,
+ UPOWER_DEVICE_TYPE_MONITOR = 4,
+ UPOWER_DEVICE_TYPE_MOUSE = 5,
+ UPOWER_DEVICE_TYPE_KEYBOARD = 6,
+ UPOWER_DEVICE_TYPE_PDA = 7,
+ UPOWER_DEVICE_TYPE_PHONE = 8,
+};
+
+typedef std::vector<dbus::ObjectPath> PathsVector;
+
+double GetPropertyAsDouble(const base::DictionaryValue& dictionary,
+ const std::string& property_name,
+ double default_value) {
+ double value = default_value;
+ return dictionary.GetDouble(property_name, &value) ? value : default_value;
+}
+
+bool GetPropertyAsBoolean(const base::DictionaryValue& dictionary,
+ const std::string& property_name,
+ bool default_value) {
+ bool value = default_value;
+ return dictionary.GetBoolean(property_name, &value) ? value : default_value;
+}
+
+scoped_ptr<base::DictionaryValue> GetPropertiesAsDictionary(
+ dbus::ObjectProxy* proxy) {
+ dbus::MethodCall method_call(dbus::kPropertiesInterface,
+ dbus::kPropertiesGetAll);
+ dbus::MessageWriter builder(&method_call);
+ builder.AppendString(kUPowerDeviceName);
+
+ scoped_ptr<dbus::Response> response(
+ proxy->CallMethodAndBlock(&method_call,
+ dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
+ if (response) {
+ dbus::MessageReader reader(response.get());
+ scoped_ptr<base::Value> value(dbus::PopDataAsValue(&reader));
+ base::DictionaryValue* dictionary_value = NULL;
+ if (value && value->GetAsDictionary(&dictionary_value)) {
+ ignore_result(value.release());
+ return scoped_ptr<base::DictionaryValue>(dictionary_value);
+ }
+ }
+ return scoped_ptr<base::DictionaryValue>();
+}
+
+scoped_ptr<PathsVector> GetPowerSourcesPaths(dbus::ObjectProxy* proxy) {
+ scoped_ptr<PathsVector> paths(new PathsVector());
+ if (!proxy)
+ return paths.Pass();
+
+ dbus::MethodCall method_call(kUPowerServiceName, kUPowerEnumerateDevices);
+ scoped_ptr<dbus::Response> response(
+ proxy->CallMethodAndBlock(&method_call,
+ dbus::ObjectProxy::TIMEOUT_USE_DEFAULT));
+
+ if (response) {
+ dbus::MessageReader reader(response.get());
+ reader.PopArrayOfObjectPaths(paths.get());
+ }
+ return paths.Pass();;
+}
+
+// Class that represents a dedicated thread which communicates with DBus to
+// obtain battery information and receives battery change notifications.
+class BatteryStatusNotificationThread : public base::Thread {
+ public:
+ BatteryStatusNotificationThread(
+ const BatteryStatusService::BatteryUpdateCallback& callback)
+ : base::Thread(kBatteryNotifierThreadName),
+ callback_(callback),
+ battery_proxy_(NULL) {}
+
+ virtual ~BatteryStatusNotificationThread() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ // Make sure to shutdown the dbus connection if it is still open in the very
+ // end. It needs to happen on the BatteryStatusNotificationThread.
+ message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BatteryStatusNotificationThread::ShutdownDBusConnection,
+ base::Unretained(this)));
+
+ // Drain the message queue of the BatteryStatusNotificationThread and stop.
+ Stop();
+ }
+
+ void StartListening() {
+ DCHECK(OnWatcherThread());
+
+ if (system_bus_)
+ return;
+
+ InitDBus();
+ dbus::ObjectProxy* power_proxy =
+ system_bus_->GetObjectProxy(kUPowerServiceName,
+ dbus::ObjectPath(kUPowerPath));
+ scoped_ptr<PathsVector> device_paths = GetPowerSourcesPaths(power_proxy);
+
+ for (size_t i = 0; i < device_paths->size(); ++i) {
+ const dbus::ObjectPath& device_path = device_paths->at(i);
+ dbus::ObjectProxy* device_proxy = system_bus_->GetObjectProxy(
+ kUPowerServiceName, device_path);
+ scoped_ptr<base::DictionaryValue> dictionary =
+ GetPropertiesAsDictionary(device_proxy);
+
+ if (!dictionary)
+ continue;
+
+ bool is_present = GetPropertyAsBoolean(*dictionary, "IsPresent", false);
+ uint32 type = static_cast<uint32>(
+ GetPropertyAsDouble(*dictionary, "Type", UPOWER_DEVICE_TYPE_UNKNOWN));
+
+ if (!is_present || type != UPOWER_DEVICE_TYPE_BATTERY) {
+ system_bus_->RemoveObjectProxy(kUPowerServiceName,
+ device_path,
+ base::Bind(&base::DoNothing));
+ continue;
+ }
+
+ if (battery_proxy_) {
+ // TODO(timvolodine): add support for multiple batteries. Currently we
+ // only collect information from the first battery we encounter
+ // (crbug.com/400780).
+ // TODO(timvolodine): add UMA logging for this case.
+ LOG(WARNING) << "multiple batteries found, "
+ << "using status data of the first battery only.";
+ } else {
+ battery_proxy_ = device_proxy;
+ }
+ }
+
+ if (!battery_proxy_) {
+ callback_.Run(blink::WebBatteryStatus());
+ return;
+ }
+
+ battery_proxy_->ConnectToSignal(
+ kUPowerDeviceName,
+ kUPowerDeviceSignalChanged,
+ base::Bind(&BatteryStatusNotificationThread::BatteryChanged,
+ base::Unretained(this)),
+ base::Bind(&BatteryStatusNotificationThread::OnSignalConnected,
+ base::Unretained(this)));
+ }
+
+ void StopListening() {
+ DCHECK(OnWatcherThread());
+ ShutdownDBusConnection();
+ }
+
+ private:
+ bool OnWatcherThread() {
+ return task_runner()->BelongsToCurrentThread();
+ }
+
+ void InitDBus() {
+ DCHECK(OnWatcherThread());
+
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SYSTEM;
+ options.connection_type = dbus::Bus::PRIVATE;
+ system_bus_ = new dbus::Bus(options);
+ }
+
+ void ShutdownDBusConnection() {
+ DCHECK(OnWatcherThread());
+
+ if (!system_bus_)
+ return;
+
+ // Shutdown DBus connection later because there may be pending tasks on
+ // this thread.
+ message_loop()->PostTask(FROM_HERE,
+ base::Bind(&dbus::Bus::ShutdownAndBlock,
+ system_bus_));
+ system_bus_ = NULL;
+ battery_proxy_ = NULL;
+ }
+
+ void OnSignalConnected(const std::string& interface_name,
+ const std::string& signal_name,
+ bool success) {
+ DCHECK(OnWatcherThread());
+
+ if (interface_name != kUPowerDeviceName ||
+ signal_name != kUPowerDeviceSignalChanged) {
+ return;
+ }
+
+ if (!system_bus_)
+ return;
+
+ if (success) {
+ BatteryChanged(NULL);
+ } else {
+ // Failed to register for "Changed" signal, execute callback with the
+ // default values.
+ callback_.Run(blink::WebBatteryStatus());
+ }
+ }
+
+ void BatteryChanged(dbus::Signal* signal /* unsused */) {
+ DCHECK(OnWatcherThread());
+
+ if (!system_bus_)
+ return;
+
+ scoped_ptr<base::DictionaryValue> dictionary =
+ GetPropertiesAsDictionary(battery_proxy_);
+ if (dictionary)
+ callback_.Run(ComputeWebBatteryStatus(*dictionary));
+ else
+ callback_.Run(blink::WebBatteryStatus());
+ }
+
+ BatteryStatusService::BatteryUpdateCallback callback_;
+ scoped_refptr<dbus::Bus> system_bus_;
+ dbus::ObjectProxy* battery_proxy_; // owned by the bus
+
+ DISALLOW_COPY_AND_ASSIGN(BatteryStatusNotificationThread);
+};
+
+// Runs on IO thread and creates a notification thread and delegates Start/Stop
+// calls to it.
+class BatteryStatusManagerLinux : public BatteryStatusManager {
+ public:
+ explicit BatteryStatusManagerLinux(
+ const BatteryStatusService::BatteryUpdateCallback& callback)
+ : callback_(callback) {}
+
+ virtual ~BatteryStatusManagerLinux() {}
+
+ private:
+ // BatteryStatusManager:
+ virtual bool StartListeningBatteryChange() OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (!StartNotifierThreadIfNecessary())
+ return false;
+
+ notifier_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BatteryStatusNotificationThread::StartListening,
+ base::Unretained(notifier_thread_.get())));
+ return true;
+ }
+
+ virtual void StopListeningBatteryChange() OVERRIDE {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
+
+ if (!notifier_thread_)
+ return;
+
+ notifier_thread_->message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&BatteryStatusNotificationThread::StopListening,
+ base::Unretained(notifier_thread_.get())));
+ }
+
+ // Starts the notifier thread if not already started and returns true on
+ // success.
+ bool StartNotifierThreadIfNecessary() {
+ if (notifier_thread_)
+ return true;
+
+ base::Thread::Options thread_options(base::MessageLoop::TYPE_IO, 0);
+ notifier_thread_.reset(new BatteryStatusNotificationThread(callback_));
+ if (!notifier_thread_->StartWithOptions(thread_options)) {
+ notifier_thread_.reset();
+ LOG(ERROR) << "Could not start the " << kBatteryNotifierThreadName
+ << " thread";
+ return false;
+ }
+ return true;
+ }
+
+ BatteryStatusService::BatteryUpdateCallback callback_;
+ scoped_ptr<BatteryStatusNotificationThread> notifier_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerLinux);
+};
+
+} // namespace
+
+blink::WebBatteryStatus ComputeWebBatteryStatus(
+ const base::DictionaryValue& dictionary) {
+ blink::WebBatteryStatus status;
+ if (!dictionary.HasKey("State"))
+ return status;
+
+ uint32 state = static_cast<uint32>(
+ GetPropertyAsDouble(dictionary, "State", UPOWER_DEVICE_STATE_UNKNOWN));
+ status.charging = state != UPOWER_DEVICE_STATE_DISCHARGING &&
+ state != UPOWER_DEVICE_STATE_EMPTY;
+ double percentage = GetPropertyAsDouble(dictionary, "Percentage", 100);
+ // Convert percentage to a value between 0 and 1 with 2 digits of precision.
+ // This is to bring it in line with other platforms like Mac and Android where
+ // we report level with 1% granularity. It also serves the purpose of reducing
+ // the possibility of fingerprinting and triggers less level change events on
+ // the blink side.
+ // TODO(timvolodine): consider moving this rounding to the blink side.
+ status.level = round(percentage) / 100.f;
+
+ switch (state) {
+ case UPOWER_DEVICE_STATE_CHARGING : {
+ double time_to_full = GetPropertyAsDouble(dictionary, "TimeToFull", 0);
+ status.chargingTime =
+ (time_to_full > 0) ? time_to_full
+ : std::numeric_limits<double>::infinity();
+ break;
+ }
+ case UPOWER_DEVICE_STATE_DISCHARGING : {
+ double time_to_empty = GetPropertyAsDouble(dictionary, "TimeToEmpty", 0);
+ // Set dischargingTime if it's available. Otherwise leave the default
+ // value which is +infinity.
+ if (time_to_empty > 0)
+ status.dischargingTime = time_to_empty;
+ status.chargingTime = std::numeric_limits<double>::infinity();
+ break;
+ }
+ case UPOWER_DEVICE_STATE_FULL : {
+ break;
+ }
+ default: {
+ status.chargingTime = std::numeric_limits<double>::infinity();
+ }
+ }
+ return status;
+}
+
+// static
+scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
+ const BatteryStatusService::BatteryUpdateCallback& callback) {
+ return scoped_ptr<BatteryStatusManager>(
+ new BatteryStatusManagerLinux(callback));
+}
+
+} // namespace content

Powered by Google App Engine
This is Rietveld 408576698