Commit 523f6b74 authored by dtrainor@chromium.org's avatar dtrainor@chromium.org

Revert 289372 "Battery Status API: implementation for Linux."

Broke Android Tests (dbg)

> Battery Status API: implementation for Linux.
> 
> Implementation of the Battery Status API for the Linux platform.
> Implementation uses DBus to talk to org.freedesktop.UPower service
> to obtain battery information.
> 
> BUG=122593
> TEST=http://jsbin.com/battery-status-test (manual)
> TBR=brettw@chromium.org
> NOTRY=true
> 
> Review URL: https://codereview.chromium.org/436683002

TBR=timvolodine@chromium.org

Review URL: https://codereview.chromium.org/472643002

Cr-Commit-Position: refs/heads/master@{#289379}
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289379 0039d316-1c4b-4281-b951-d872f2087c98
parent 6a831d37
......@@ -402,16 +402,12 @@ source_set("browser") {
if (is_linux) {
if (use_dbus) {
sources -= [
"battery_status/battery_status_manager_default.cc",
"geolocation/empty_wifi_data_provider.cc",
]
deps += [ "//dbus" ]
} else {
# This will already have gotten removed for all non-Linux cases.
sources -= [
"battery_status/battery_status_manager_linux.cc",
"geolocation/wifi_data_provider_linux.cc",
]
sources -= [ "geolocation/wifi_data_provider_linux.cc" ]
}
}
}
// 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
// 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.
#ifndef CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_LINUX_H_
#define CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_LINUX_H_
#include "content/common/content_export.h"
#include "third_party/WebKit/public/platform/WebBatteryStatus.h"
namespace base {
class DictionaryValue;
}
namespace content {
// UPowerDeviceState reflects the possible UPower.Device.State values,
// see upower.freedesktop.org/docs/Device.html#Device:State.
enum UPowerDeviceState {
UPOWER_DEVICE_STATE_UNKNOWN = 0,
UPOWER_DEVICE_STATE_CHARGING = 1,
UPOWER_DEVICE_STATE_DISCHARGING = 2,
UPOWER_DEVICE_STATE_EMPTY = 3,
UPOWER_DEVICE_STATE_FULL = 4,
UPOWER_DEVICE_STATE_PENDING_CHARGE = 5,
UPOWER_DEVICE_STATE_PENDING_DISCHARGE = 6,
};
// Returns the WebBatteryStatus computed using the provided dictionary.
CONTENT_EXPORT blink::WebBatteryStatus ComputeWebBatteryStatus(
const base::DictionaryValue& dictionary);
} // namespace content
#endif // CHROME_BROWSER_BATTERY_STATUS_BATTERY_STATUS_MANAGER_LINUX_H_
// 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/values.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace content {
namespace {
TEST(BatteryStatusManagerLinuxTest, EmptyDictionary) {
base::DictionaryValue dictionary;
blink::WebBatteryStatus default_status;
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_EQ(default_status.charging, status.charging);
EXPECT_EQ(default_status.chargingTime, status.chargingTime);
EXPECT_EQ(default_status.dischargingTime, status.dischargingTime);
EXPECT_EQ(default_status.level, status.level);
}
TEST(BatteryStatusManagerLinuxTest, ChargingHalfFull) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_CHARGING);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("Percentage", 50);
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_TRUE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.chargingTime);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.dischargingTime);
EXPECT_EQ(0.5, status.level);
}
TEST(BatteryStatusManagerLinuxTest, ChargingTimeToFull) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_CHARGING);
dictionary.SetDouble("TimeToFull", 100.f);
dictionary.SetDouble("Percentage", 1);
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_TRUE(status.charging);
EXPECT_EQ(100, status.chargingTime);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.dischargingTime);
EXPECT_EQ(.01, status.level);
}
TEST(BatteryStatusManagerLinuxTest, FullyCharged) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_FULL);
dictionary.SetDouble("TimeToFull", 100);
dictionary.SetDouble("TimeToEmpty", 200);
dictionary.SetDouble("Percentage", 100);
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_TRUE(status.charging);
EXPECT_EQ(0, status.chargingTime);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.dischargingTime);
EXPECT_EQ(1, status.level);
}
TEST(BatteryStatusManagerLinuxTest, Discharging) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("TimeToEmpty", 200);
dictionary.SetDouble("Percentage", 90);
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.chargingTime);
EXPECT_EQ(200, status.dischargingTime);
EXPECT_EQ(.9, status.level);
}
TEST(BatteryStatusManagerLinuxTest, DischargingTimeToEmptyUnknown) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("TimeToEmpty", 0);
dictionary.SetDouble("Percentage", 90);
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.chargingTime);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.dischargingTime);
EXPECT_EQ(.9, status.level);
}
TEST(BatteryStatusManagerLinuxTest, DeviceStateUnknown) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_UNKNOWN);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("TimeToEmpty", 0);
dictionary.SetDouble("Percentage", 50);
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_TRUE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.chargingTime);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.dischargingTime);
EXPECT_EQ(.5, status.level);
}
TEST(BatteryStatusManagerLinuxTest, DeviceStateEmpty) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_EMPTY);
dictionary.SetDouble("TimeToFull", 0);
dictionary.SetDouble("TimeToEmpty", 0);
dictionary.SetDouble("Percentage", 0);
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.chargingTime);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.dischargingTime);
EXPECT_EQ(0, status.level);
}
TEST(BatteryStatusManagerLinuxTest, LevelRoundedToThreeSignificantDigits) {
base::DictionaryValue dictionary;
dictionary.SetDouble("State", UPOWER_DEVICE_STATE_DISCHARGING);
dictionary.SetDouble("Percentage", 14.56);
blink::WebBatteryStatus status = ComputeWebBatteryStatus(dictionary);
EXPECT_FALSE(status.charging);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.chargingTime);
EXPECT_EQ(std::numeric_limits<double>::infinity(), status.dischargingTime);
EXPECT_EQ(0.15, status.level);
}
} // namespace
} // namespace content
......@@ -374,8 +374,6 @@
'browser/battery_status/battery_status_manager_android.h',
'browser/battery_status/battery_status_manager_chromeos.cc',
'browser/battery_status/battery_status_manager_default.cc',
'browser/battery_status/battery_status_manager_linux.cc',
'browser/battery_status/battery_status_manager_linux.h',
'browser/battery_status/battery_status_manager_mac.cc',
'browser/battery_status/battery_status_manager.h',
'browser/battery_status/battery_status_message_filter.cc',
......@@ -1851,7 +1849,6 @@
}],
['OS == "linux" and use_dbus==1', {
'sources!': [
'browser/battery_status/battery_status_manager_default.cc',
'browser/geolocation/empty_wifi_data_provider.cc',
],
'dependencies': [
......@@ -1860,7 +1857,6 @@
],
}, { # OS != "linux" or use_dbus==0
'sources!': [
'browser/battery_status/battery_status_manager_linux.cc',
'browser/geolocation/wifi_data_provider_linux.cc',
],
}],
......
......@@ -404,7 +404,6 @@
'browser/appcache/mock_appcache_storage.cc',
'browser/appcache/mock_appcache_storage.h',
'browser/appcache/mock_appcache_storage_unittest.cc',
'browser/battery_status/battery_status_manager_linux_unittest.cc',
'browser/battery_status/battery_status_service_unittest.cc',
'browser/browser_thread_unittest.cc',
'browser/browser_url_handler_impl_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment