| Index: content/browser/power_save_blocker_linux.cc
|
| ===================================================================
|
| --- content/browser/power_save_blocker_linux.cc (revision 141311)
|
| +++ content/browser/power_save_blocker_linux.cc (working copy)
|
| @@ -34,483 +34,8 @@
|
| #include "dbus/object_path.h"
|
| #include "dbus/object_proxy.h"
|
|
|
| -using content::BrowserThread;
|
| -
|
| namespace {
|
|
|
| -// This class is used to inhibit Power Management on Linux systems using D-Bus
|
| -// interfaces. Mainly, there are two interfaces that make this possible.
|
| -// org.freedesktop.PowerManagement[.Inhibit] is considered to be the
|
| -// desktop-agnostic solution. However, it is only used by KDE4 and XFCE.
|
| -//
|
| -// org.gnome.SessionManager is the Power Management interface available on GNOME
|
| -// desktops. Given that there is no generic solution to this problem, this class
|
| -// delegates the task of calling specific D-Bus APIs, to a
|
| -// DBusPowerSaveBlock::Delegate object.
|
| -//
|
| -// This class is a Singleton and the delegate will be instantiated internally,
|
| -// when the singleton instance is created, based on the desktop environment in
|
| -// which the application is running. When the class is instantiated, if it runs
|
| -// under a supported desktop environment it creates the Bus object and the
|
| -// delegate. Otherwise, no object is created and the ApplyBlock method will not
|
| -// do anything.
|
| -class DBusPowerSaveBlocker {
|
| - public:
|
| - // String passed to D-Bus APIs as the reason for which
|
| - // the power management features are temporarily disabled.
|
| - static const char kPowerSaveReason[];
|
| -
|
| - // This delegate interface represents a concrete implementation for a specific
|
| - // D-Bus interface. It is responsible for obtaining specific object proxies,
|
| - // making D-Bus method calls and handling D-Bus responses.
|
| - //
|
| - // When a new DBusPowerBlocker is created, only a specific implementation of
|
| - // the delegate is instantiated. See the DBusPowerSaveBlocker constructor for
|
| - // more details. This is ref_counted to make sure that the callbacks stay
|
| - // alive even after the DBusPowerSaveBlocker object is deleted.
|
| - class Delegate : public base::RefCountedThreadSafe<Delegate> {
|
| - public:
|
| - Delegate() {}
|
| - virtual void ApplyBlock(PowerSaveBlocker::PowerSaveBlockerType type) = 0;
|
| -
|
| - protected:
|
| - virtual ~Delegate() {}
|
| -
|
| - private:
|
| - friend class base::RefCountedThreadSafe<Delegate>;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(Delegate);
|
| - };
|
| -
|
| - // Returns a pointer to the sole instance of this class
|
| - static DBusPowerSaveBlocker* GetInstance();
|
| -
|
| - // Forwards a power save block request to the concrete implementation of the
|
| - // Delegate interface. If |delegate_| is NULL, the application runs under an
|
| - // unsupported desktop environment. In this case, the method does nothing.
|
| - void ApplyBlock(PowerSaveBlocker::PowerSaveBlockerType type) {
|
| - if (delegate_)
|
| - delegate_->ApplyBlock(type);
|
| - }
|
| -
|
| - // Getter for the Bus object. Used by the Delegates to obtain object proxies.
|
| - scoped_refptr<dbus::Bus> bus() const { return bus_; }
|
| -
|
| - private:
|
| - DBusPowerSaveBlocker();
|
| - virtual ~DBusPowerSaveBlocker();
|
| -
|
| - // If DPMS is not enabled, then we don't want to try to disable power saving,
|
| - // since on some desktop environments that may enable DPMS with very poor
|
| - // default settings (e.g. turning off the display after only 1 second).
|
| - static bool DPMSEnabled();
|
| -
|
| - // The D-Bus connection.
|
| - scoped_refptr<dbus::Bus> bus_;
|
| -
|
| - // Concrete implementation of the Delegate interface.
|
| - scoped_refptr<Delegate> delegate_;
|
| -
|
| - friend struct DefaultSingletonTraits<DBusPowerSaveBlocker>;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(DBusPowerSaveBlocker);
|
| -};
|
| -
|
| -// Delegate implementation for KDE4. It uses the
|
| -// org.freedesktop.PowerManagement interface. It works on XFCE4, too.
|
| -class KDEPowerSaveBlocker : public DBusPowerSaveBlocker::Delegate {
|
| - public:
|
| - KDEPowerSaveBlocker()
|
| - : inhibit_cookie_(0),
|
| - pending_inhibit_call_(false),
|
| - postponed_uninhibit_call_(false) {
|
| - }
|
| -
|
| - virtual void ApplyBlock(
|
| - PowerSaveBlocker::PowerSaveBlockerType type) OVERRIDE {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(pending_inhibit_call_ || !postponed_uninhibit_call_);
|
| -
|
| - // If we have a pending inhibit call, we add a postponed uninhibit
|
| - // request, such that it will be canceled as soon as the response arrives.
|
| - // If we have an active inhibit request and receive a new one,
|
| - // we ignore it since the 'freedesktop' interface has only one Inhibit level
|
| - // and we cannot differentiate between SystemSleep and DisplaySleep,
|
| - // so there's no need to make additional D-Bus method calls.
|
| - if (type == PowerSaveBlocker::kPowerSaveBlockPreventNone) {
|
| - if (pending_inhibit_call_ && postponed_uninhibit_call_) {
|
| - return;
|
| - } else if (pending_inhibit_call_ && !postponed_uninhibit_call_) {
|
| - postponed_uninhibit_call_ = true;
|
| - return;
|
| - } else if (!pending_inhibit_call_ && inhibit_cookie_ == 0) {
|
| - return;
|
| - }
|
| - } else if ((pending_inhibit_call_ && !postponed_uninhibit_call_) ||
|
| - inhibit_cookie_ > 0) {
|
| - return;
|
| - }
|
| -
|
| - scoped_refptr<dbus::ObjectProxy> object_proxy =
|
| - DBusPowerSaveBlocker::GetInstance()->bus()->GetObjectProxy(
|
| - "org.freedesktop.PowerManagement",
|
| - dbus::ObjectPath("/org/freedesktop/PowerManagement/Inhibit"));
|
| - dbus::MethodCall method_call("org.freedesktop.PowerManagement.Inhibit",
|
| - "Inhibit");
|
| - dbus::MessageWriter message_writer(&method_call);
|
| - base::Callback<void(dbus::Response*)> bus_callback;
|
| -
|
| - switch (type) {
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep:
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventSystemSleep:
|
| - // The org.freedesktop.PowerManagement.Inhibit interface offers only one
|
| - // Inhibit() method, that temporarily disables all power management
|
| - // features. We cannot differentiate and disable individual features,
|
| - // like display sleep or system sleep.
|
| - // The first argument of the Inhibit method is the application name.
|
| - // The second argument of the Inhibit method is a string containing
|
| - // the reason of the power save block request.
|
| - // The method returns a cookie (an int), which we must pass back to the
|
| - // UnInhibit method when we cancel our request.
|
| - message_writer.AppendString(
|
| - CommandLine::ForCurrentProcess()->GetProgram().value());
|
| - message_writer.AppendString(DBusPowerSaveBlocker::kPowerSaveReason);
|
| - bus_callback = base::Bind(&KDEPowerSaveBlocker::OnInhibitResponse,
|
| - this);
|
| - pending_inhibit_call_ = true;
|
| - break;
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventNone:
|
| - // To cancel our inhibit request, we have to call a different method.
|
| - // It takes one argument, the cookie returned by the corresponding
|
| - // Inhibit method call.
|
| - method_call.SetMember("UnInhibit");
|
| - message_writer.AppendUint32(inhibit_cookie_);
|
| - bus_callback = base::Bind(&KDEPowerSaveBlocker::OnUnInhibitResponse,
|
| - this);
|
| - break;
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventStateCount:
|
| - // This is an invalid argument
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| -
|
| - object_proxy->CallMethod(&method_call,
|
| - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
|
| - bus_callback);
|
| - }
|
| -
|
| - protected:
|
| - virtual ~KDEPowerSaveBlocker() {}
|
| -
|
| - private:
|
| - // Inhibit() response callback.
|
| - // Stores the cookie so we can use it later when calling UnInhibit().
|
| - // If the response from D-Bus is successful and there is a postponed
|
| - // uninhibit request, we cancel the cookie that we just received.
|
| - void OnInhibitResponse(dbus::Response* response) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(pending_inhibit_call_);
|
| - pending_inhibit_call_ = false;
|
| - if (response) {
|
| - dbus::MessageReader message_reader(response);
|
| - if (message_reader.PopUint32(&inhibit_cookie_)) {
|
| - if (postponed_uninhibit_call_) {
|
| - postponed_uninhibit_call_ = false;
|
| - ApplyBlock(PowerSaveBlocker::kPowerSaveBlockPreventNone);
|
| - }
|
| - return;
|
| - } else {
|
| - LOG(ERROR) << "Invalid Inhibit() response: " << response->ToString();
|
| - }
|
| - }
|
| - inhibit_cookie_ = 0;
|
| - postponed_uninhibit_call_ = false;
|
| - }
|
| -
|
| - // UnInhibit() method callback.
|
| - // We set the |inhibit_cookie_| to 0 even if the D-Bus call failed.
|
| - void OnUnInhibitResponse(dbus::Response* response) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - inhibit_cookie_ = 0;
|
| - };
|
| -
|
| - // The cookie that identifies our last inhibit request,
|
| - // or 0 if there is no active inhibit request.
|
| - uint32 inhibit_cookie_;
|
| -
|
| - // True if we made an inhibit call for which
|
| - // we did not receive a response yet
|
| - bool pending_inhibit_call_;
|
| -
|
| - // True if we have to cancel the cookie we are about to receive
|
| - bool postponed_uninhibit_call_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(KDEPowerSaveBlocker);
|
| -};
|
| -
|
| -// Delegate implementation for Gnome, based on org.gnome.SessionManager
|
| -class GnomePowerSaveBlocker : public DBusPowerSaveBlocker::Delegate {
|
| - public:
|
| - // Inhibit flags defined in the org.gnome.SessionManager interface.
|
| - // Can be OR'd together and passed as argument to the Inhibit() method
|
| - // to specify which power management features we want to suspend.
|
| - enum InhibitFlags {
|
| - kInhibitLogOut = 1,
|
| - kInhibitSwitchUser = 2,
|
| - kInhibitSuspendSession = 4,
|
| - kInhibitMarkSessionAsIdle = 8
|
| - };
|
| -
|
| - GnomePowerSaveBlocker()
|
| - : inhibit_cookie_(0),
|
| - pending_inhibit_calls_(0),
|
| - postponed_uninhibit_calls_(0) {}
|
| -
|
| - virtual void ApplyBlock(
|
| - PowerSaveBlocker::PowerSaveBlockerType type) OVERRIDE {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK(postponed_uninhibit_calls_ <= pending_inhibit_calls_);
|
| -
|
| - // If we have a pending inhibit call, we add a postponed uninhibit request,
|
| - // such that it will be canceled as soon as the response arrives. We want to
|
| - // cancel the current inhibit request whether |type| is
|
| - // kPowerSaveBlockPreventNone or not. If |type| represents an inhibit
|
| - // request, we are dealing with the same case as below, just that the reply
|
| - // to the previous inhibit request did not arrive yet, so we have to wait
|
| - // for the cookie in order to cancel it. Meanwhile, we can still make the
|
| - // new request.
|
| - //
|
| - // We also have to check that postponed_uninhibit_calls_ <
|
| - // pending_inhibit_calls_. If this is not the case, then all the pending
|
| - // requests were already canceled and we should not increment the number of
|
| - // postponed uninhibit requests; otherwise we will cancel unwanted future
|
| - // inhibits, that will be made after this call.
|
| - //
|
| - // NOTE: The implementation is based on the fact that we receive the D-Bus
|
| - // replies in the same order in which the requests are made.
|
| - if (pending_inhibit_calls_ > 0 &&
|
| - postponed_uninhibit_calls_ < pending_inhibit_calls_) {
|
| - ++postponed_uninhibit_calls_;
|
| - // If the call was an Uninhibit, then we are done for the moment.
|
| - if (type == PowerSaveBlocker::kPowerSaveBlockPreventNone)
|
| - return;
|
| - }
|
| -
|
| - // If we have an active inhibit request and no pending inhibit calls,
|
| - // we make an uninhibit request to cancel it now.
|
| - if (type != PowerSaveBlocker::kPowerSaveBlockPreventNone &&
|
| - pending_inhibit_calls_ == 0 &&
|
| - inhibit_cookie_ > 0) {
|
| - ApplyBlock(PowerSaveBlocker::kPowerSaveBlockPreventNone);
|
| - }
|
| -
|
| - static const char kGnomeSessionManagerName[] = "org.gnome.SessionManager";
|
| - scoped_refptr<dbus::ObjectProxy> object_proxy =
|
| - DBusPowerSaveBlocker::GetInstance()->bus()->GetObjectProxy(
|
| - kGnomeSessionManagerName,
|
| - dbus::ObjectPath("/org/gnome/SessionManager"));
|
| - dbus::MethodCall method_call(kGnomeSessionManagerName, "Inhibit");
|
| - dbus::MessageWriter message_writer(&method_call);
|
| - base::Callback<void(dbus::Response*)> bus_callback;
|
| -
|
| - unsigned int flags = 0;
|
| - switch (type) {
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep:
|
| - flags |= kInhibitMarkSessionAsIdle;
|
| - break;
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventSystemSleep:
|
| - flags |= kInhibitMarkSessionAsIdle;
|
| - flags |= kInhibitSuspendSession;
|
| - break;
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventNone:
|
| - break;
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventStateCount:
|
| - // This is an invalid argument
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| -
|
| - switch (type) {
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventDisplaySleep:
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventSystemSleep:
|
| - // To temporarily suspend the power management features on Gnome,
|
| - // we call org.gnome.SessionManager.Inhibit().
|
| - // The arguments of the method are:
|
| - // app_id: The application identifier
|
| - // toplevel_xid: The toplevel X window identifier
|
| - // reason: The reason for the inhibit
|
| - // flags: Flags that spefify what should be inhibited
|
| - // The method returns and inhibit_cookie, used to uniquely identify
|
| - // this request. It should be used as an argument to Uninhibit()
|
| - // in order to remove the request.
|
| - message_writer.AppendString(
|
| - CommandLine::ForCurrentProcess()->GetProgram().value());
|
| - message_writer.AppendUint32(0); // should be toplevel_xid
|
| - message_writer.AppendString(DBusPowerSaveBlocker::kPowerSaveReason);
|
| - message_writer.AppendUint32(flags);
|
| - bus_callback = base::Bind(&GnomePowerSaveBlocker::OnInhibitResponse,
|
| - this);
|
| - ++pending_inhibit_calls_;
|
| - break;
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventNone:
|
| - // To cancel a previous inhibit request we call
|
| - // org.gnome.SessionManager.Uninhibit().
|
| - // It takes only one argument, the cookie that identifies
|
| - // the request we want to cancel.
|
| - method_call.SetMember("Uninhibit");
|
| - message_writer.AppendUint32(inhibit_cookie_);
|
| - bus_callback = base::Bind(&GnomePowerSaveBlocker::OnUnInhibitResponse,
|
| - this);
|
| - ++pending_inhibit_calls_;
|
| - break;
|
| - case PowerSaveBlocker::kPowerSaveBlockPreventStateCount:
|
| - // This is an invalid argument.
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| -
|
| - object_proxy->CallMethod(&method_call,
|
| - dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
|
| - bus_callback);
|
| - }
|
| -
|
| - protected:
|
| - virtual ~GnomePowerSaveBlocker() {}
|
| -
|
| - private:
|
| - // Inhibit() response callback.
|
| - // Stores the cookie so we can use it later when calling UnInhibit().
|
| - // If the response from D-Bus is successful and there is a postponed
|
| - // uninhibit request, we cancel the cookie that we just received.
|
| - void OnInhibitResponse(dbus::Response* response) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DCHECK_GT(pending_inhibit_calls_, 0);
|
| - --pending_inhibit_calls_;
|
| - if (response) {
|
| - dbus::MessageReader message_reader(response);
|
| - if (message_reader.PopUint32(&inhibit_cookie_)) {
|
| - if (postponed_uninhibit_calls_ > 0) {
|
| - --postponed_uninhibit_calls_;
|
| - ApplyBlock(PowerSaveBlocker::kPowerSaveBlockPreventNone);
|
| - }
|
| - return;
|
| - } else {
|
| - LOG(ERROR) << "Invalid Inhibit() response: " << response->ToString();
|
| - }
|
| - }
|
| - inhibit_cookie_ = 0;
|
| - if (postponed_uninhibit_calls_ > 0) {
|
| - --postponed_uninhibit_calls_;
|
| - }
|
| - }
|
| -
|
| - // Uninhibit() response callback.
|
| - // We set the |inhibit_cookie_| to 0 even if the D-Bus call failed.
|
| - void OnUnInhibitResponse(dbus::Response* response) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - inhibit_cookie_ = 0;
|
| - };
|
| -
|
| - // The cookie that identifies our last inhibit request,
|
| - // or 0 if there is no active inhibit request.
|
| - uint32 inhibit_cookie_;
|
| -
|
| - // Store the number of inhibit calls for which
|
| - // we did not receive a response yet
|
| - int pending_inhibit_calls_;
|
| -
|
| - // Store the number of Uninhibit requests that arrived,
|
| - // before the corresponding Inhibit calls were completed.
|
| - int postponed_uninhibit_calls_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(GnomePowerSaveBlocker);
|
| -};
|
| -
|
| -const char DBusPowerSaveBlocker::kPowerSaveReason[] = "Power Save Blocker";
|
| -
|
| -// Initialize the DBusPowerSaveBlocker instance:
|
| -// 1. Instantiate a concrete delegate based on the current desktop environment,
|
| -// 2. Instantiate the D-Bus object
|
| -DBusPowerSaveBlocker::DBusPowerSaveBlocker() {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| -
|
| - scoped_ptr<base::Environment> env(base::Environment::Create());
|
| - switch (base::nix::GetDesktopEnvironment(env.get())) {
|
| - case base::nix::DESKTOP_ENVIRONMENT_GNOME:
|
| - if (DPMSEnabled())
|
| - delegate_ = new GnomePowerSaveBlocker();
|
| - break;
|
| - case base::nix::DESKTOP_ENVIRONMENT_XFCE:
|
| - case base::nix::DESKTOP_ENVIRONMENT_KDE4:
|
| - if (DPMSEnabled())
|
| - delegate_ = new KDEPowerSaveBlocker();
|
| - break;
|
| - case base::nix::DESKTOP_ENVIRONMENT_KDE3:
|
| - case base::nix::DESKTOP_ENVIRONMENT_OTHER:
|
| - // Not supported, so we exit.
|
| - // We don't create D-Bus objects.
|
| - break;
|
| - }
|
| -
|
| - if (delegate_) {
|
| - dbus::Bus::Options options;
|
| - options.bus_type = dbus::Bus::SESSION;
|
| - options.connection_type = dbus::Bus::PRIVATE;
|
| - // Use the FILE thread to service the D-Bus connection,
|
| - // since we need a thread that allows I/O operations.
|
| - options.dbus_thread_message_loop_proxy =
|
| - BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE);
|
| - bus_ = new dbus::Bus(options);
|
| - }
|
| -}
|
| -
|
| -DBusPowerSaveBlocker::~DBusPowerSaveBlocker() {
|
| - // We try to shut down the bus, but unfortunately in most of the
|
| - // cases when we delete the singleton instance,
|
| - // the FILE thread is already stopped and there is no way to
|
| - // shutdown the bus object on the origin thread (the UI thread).
|
| - // However, this is not a crucial problem since at this point
|
| - // we are at the very end of the shutting down phase.
|
| - // Connection to D-Bus is just a Unix domain socket, which is not
|
| - // a persistent resource, hence the operating system will take care
|
| - // of closing it when the process terminates.
|
| - if (BrowserThread::IsMessageLoopValid(BrowserThread::FILE)) {
|
| - bus_->ShutdownOnDBusThreadAndBlock();
|
| - }
|
| -}
|
| -
|
| -// static
|
| -bool DBusPowerSaveBlocker::DPMSEnabled() {
|
| - Display* display = base::MessagePumpForUI::GetDefaultXDisplay();
|
| - BOOL enabled = false;
|
| - int dummy;
|
| - if (DPMSQueryExtension(display, &dummy, &dummy) && DPMSCapable(display)) {
|
| - CARD16 state;
|
| - DPMSInfo(display, &state, &enabled);
|
| - }
|
| - return enabled;
|
| -}
|
| -
|
| -// static
|
| -DBusPowerSaveBlocker* DBusPowerSaveBlocker::GetInstance() {
|
| - return Singleton<DBusPowerSaveBlocker>::get();
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -// Called only from UI thread.
|
| -// static
|
| -void PowerSaveBlocker::ApplyBlock(PowerSaveBlockerType type) {
|
| - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| - DBusPowerSaveBlocker::GetInstance()->ApplyBlock(type);
|
| -}
|
| -
|
| -// TODO(mdm): remove from the beginning of the file up to (and including) this
|
| -// line to switch to the new implementation.
|
| -#define PowerSaveBlocker PowerSaveBlocker2
|
| -
|
| -namespace {
|
| -
|
| enum DBusAPI {
|
| NO_API, // Disable. No supported API available.
|
| GNOME_API, // Use the GNOME API. (Supports more features.)
|
|
|