blob: 62429a868193c808673b9b2316c89c816d7fd924 [file] [log] [blame]
// Copyright (c) 2011 The Chromium OS 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 "chromeos_power.h"
#include <dbus/dbus.h>
#include <dbus/dbus-glib.h>
#include <dbus/dbus-glib-lowlevel.h>
#include <cstdlib>
#include <cstring>
#include <exception>
#include <string>
#include <vector>
#include <base/file_util.h>
#include <base/logging.h>
#include <base/string_util.h>
#include "chromeos/dbus/dbus.h"
#include <chromeos/dbus/service_constants.h>
#include "chromeos/glib/object.h"
#include "chromeos/string.h"
namespace chromeos {
struct PowerRequestCallbackData {
PowerRequestCallbackData(GetIdleTimeCallback cb, void* obj)
: proxy(new dbus::Proxy(dbus::GetSystemBusConnection(),
power_manager::kPowerManagerInterface,
power_manager::kPowerManagerServicePath,
power_manager::kPowerManagerInterface)),
callback(cb),
object(obj) {}
scoped_ptr<dbus::Proxy> proxy;
GetIdleTimeCallback callback;
void* object;
};
// DBus will always call the Delete function passed to it by
// dbus_g_proxy_begin_call, whether DBus calls the callback or not.
void DeletePowerCallbackData(void* user_data) {
PowerRequestCallbackData* cb_data =
reinterpret_cast<PowerRequestCallbackData*>(user_data);
delete cb_data;
}
namespace { // NOLINT
bool RetrievePowerStatus(const dbus::Proxy& proxy, PowerStatus* status) {
GError* error = NULL;
// Use dbus_bool_t instead of bool because it is safer for dbus calls.
dbus_bool_t line_power_on = false;
dbus_bool_t battery_is_present = false;
dbus_bool_t battery_is_charged = false;
if(!dbus_g_proxy_call(proxy.gproxy(), power_manager::kGetAllPropertiesMethod,
&error,
G_TYPE_INVALID,
G_TYPE_BOOLEAN, &line_power_on,
G_TYPE_DOUBLE, &status->battery_energy,
G_TYPE_DOUBLE, &status->battery_energy_rate,
G_TYPE_DOUBLE, &status->battery_voltage,
G_TYPE_INT64, &status->battery_time_to_empty,
G_TYPE_INT64, &status->battery_time_to_full,
G_TYPE_DOUBLE, &status->battery_percentage,
G_TYPE_BOOLEAN, &battery_is_present,
G_TYPE_BOOLEAN, &battery_is_charged,
G_TYPE_INVALID)) {
LOG(WARNING) << (error->message ? error->message : "GetProperty failed.");
return false;
}
status->line_power_on = line_power_on;
status->battery_is_present = battery_is_present;
status->battery_state = battery_is_charged ? BATTERY_STATE_FULLY_CHARGED
: BATTERY_STATE_UNKNOWN;
return true;
}
} // namespace
class OpaquePowerStatusConnection {
public:
typedef dbus::MonitorConnection<void (const char*)>* ConnectionType;
OpaquePowerStatusConnection(const PowerStatus& status,
const dbus::Proxy& proxy,
const PowerMonitor& monitor,
void* object)
: status_(status),
proxy_(proxy),
monitor_(monitor),
object_(object),
connection_(NULL) {
}
void Run() {
if (!RetrievePowerStatus(proxy_, &status_))
return;
monitor_(object_, status_);
}
ConnectionType& connection() {
return connection_;
}
private:
PowerStatus status_;
dbus::Proxy proxy_;
PowerMonitor monitor_;
void* object_;
ConnectionType connection_;
};
namespace { // NOLINT
DBusHandlerResult DBusMessageHandler(DBusConnection* connection,
DBusMessage* message,
void* data) {
OpaquePowerStatusConnection* power_connection =
static_cast<OpaquePowerStatusConnection*>(data);
if (dbus_message_is_signal(message, power_manager::kPowerManagerInterface,
"PowerSupplyPoll")) {
power_connection->Run();
return DBUS_HANDLER_RESULT_HANDLED;
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
} // namespace
void GetIdleTimeNotify(DBusGProxy* gproxy,
DBusGProxyCall* call_id,
void* user_data) {
PowerRequestCallbackData* cb_data =
reinterpret_cast<PowerRequestCallbackData*>(
user_data);
DCHECK(cb_data);
glib::ScopedError error;
int64 time_idle_ms = 0;
if (!::dbus_g_proxy_end_call(gproxy,
call_id,
&Resetter(&error).lvalue(),
G_TYPE_INT64, &time_idle_ms,
G_TYPE_INVALID)) {
if (error->domain == DBUS_GERROR &&
error->code == DBUS_GERROR_REMOTE_EXCEPTION) {
LOG(WARNING) << "Remote DBus error";
} else {
LOG(WARNING) << "GetIdleTimeNotify error: "
<< (error->message ? error->message : "Unknown Error.");
}
cb_data->callback(cb_data->object, 0, false);
} else {
cb_data->callback(cb_data->object, time_idle_ms, true);
}
}
extern "C"
PowerStatusConnection ChromeOSMonitorPowerStatus(PowerMonitor monitor,
void* object) {
dbus::BusConnection bus = dbus::GetSystemBusConnection();
dbus::Proxy power_status_proxy(bus,
power_manager::kPowerManagerServiceName,
power_manager::kPowerManagerServicePath,
power_manager::kPowerManagerInterface);
PowerStatus status = { };
RetrievePowerStatus(power_status_proxy, &status);
monitor(object, status);
PowerStatusConnection result =
new OpaquePowerStatusConnection(status,
power_status_proxy,
monitor,
object);
DBusConnection* connection = dbus_g_connection_get_connection(
bus.g_connection());
CHECK(connection);
DBusError error;
dbus_error_init(&error);
std::string match = StringPrintf("type='signal', interface='%s'",
power_manager::kPowerManagerInterface);
dbus_bus_add_match(connection, match.c_str(), &error);
if (dbus_error_is_set(&error)) {
LOG(DFATAL) << "Failed to add match \"" << match << "\": "
<< error.name << ", message=" << error.message;
}
CHECK(dbus_connection_add_filter(connection, &DBusMessageHandler, result,
NULL));
return result;
}
extern "C"
void ChromeOSGetIdleTime(GetIdleTimeCallback callback,
void* object) {
PowerRequestCallbackData* cb_data =
new PowerRequestCallbackData(callback, object);
DBusGProxyCall* call_id =
::dbus_g_proxy_begin_call(cb_data->proxy->gproxy(),
"GetIdleTime",
&GetIdleTimeNotify,
cb_data,
&DeletePowerCallbackData,
G_TYPE_INVALID);
if (!call_id) {
LOG(ERROR) << "ChromeOSGetIdleTime call failed";
callback(object, 0, false);
delete cb_data;
}
}
extern "C"
void ChromeOSDisconnectPowerStatus(PowerStatusConnection connection) {
if (connection && connection->connection()) {
dbus::Disconnect(connection->connection());
delete connection;
}
}
extern "C"
void ChromeOSEnableScreenLock(bool enable) {
static const char kPowerManagerConfig[] =
"/var/lib/power_manager/lock_on_idle_suspend";
std::string config = base::StringPrintf("%d", enable);
file_util::WriteFile(FilePath(kPowerManagerConfig),
config.c_str(),
config.size());
}
extern "C"
void ChromeOSRequestRestart() {
chromeos::dbus::CallMethodWithNoArguments(
power_manager::kPowerManagerServiceName,
power_manager::kPowerManagerServicePath,
power_manager::kPowerManagerInterface,
power_manager::kRequestRestartMethod);
}
extern "C"
void ChromeOSRequestShutdown() {
chromeos::dbus::CallMethodWithNoArguments(
power_manager::kPowerManagerServiceName,
power_manager::kPowerManagerServicePath,
power_manager::kPowerManagerInterface,
power_manager::kRequestShutdownMethod);
}
} // namespace chromeos