普通文本  |  899行  |  32.59 KB

// Copyright (c) 2012 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 "chromeos/dbus/power_manager_client.h"

#include <algorithm>

#include "base/bind.h"
#include "base/callback.h"
#include "base/command_line.h"
#include "base/format_macros.h"
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop.h"
#include "base/observer_list.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chromeos/chromeos_switches.h"
#include "chromeos/dbus/power_manager/input_event.pb.h"
#include "chromeos/dbus/power_manager/peripheral_battery_status.pb.h"
#include "chromeos/dbus/power_manager/policy.pb.h"
#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
#include "chromeos/dbus/power_manager/suspend.pb.h"
#include "dbus/bus.h"
#include "dbus/message.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"

namespace chromeos {

// Maximum amount of time that the power manager will wait for Chrome to
// say that it's ready for the system to be suspended, in milliseconds.
const int kSuspendDelayTimeoutMs = 5000;

// Human-readable description of Chrome's suspend delay.
const char kSuspendDelayDescription[] = "chrome";

// The PowerManagerClient implementation used in production.
class PowerManagerClientImpl : public PowerManagerClient {
 public:
  PowerManagerClientImpl()
      : origin_thread_id_(base::PlatformThread::CurrentId()),
        power_manager_proxy_(NULL),
        suspend_delay_id_(-1),
        has_suspend_delay_id_(false),
        pending_suspend_id_(-1),
        suspend_is_pending_(false),
        num_pending_suspend_readiness_callbacks_(0),
        last_is_projecting_(false),
        weak_ptr_factory_(this) {}

  virtual ~PowerManagerClientImpl() {
    // Here we should unregister suspend notifications from powerd,
    // however:
    // - The lifetime of the PowerManagerClientImpl can extend past that of
    //   the objectproxy,
    // - power_manager can already detect that the client is gone and
    //   unregister our suspend delay.
  }

  // PowerManagerClient overrides:

  virtual void AddObserver(Observer* observer) OVERRIDE {
    CHECK(observer);  // http://crbug.com/119976
    observers_.AddObserver(observer);
  }

  virtual void RemoveObserver(Observer* observer) OVERRIDE {
    observers_.RemoveObserver(observer);
  }

  virtual bool HasObserver(Observer* observer) OVERRIDE {
    return observers_.HasObserver(observer);
  }

  virtual void DecreaseScreenBrightness(bool allow_off) OVERRIDE {
    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kDecreaseScreenBrightnessMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendBool(allow_off);
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        dbus::ObjectProxy::EmptyResponseCallback());
  }

  virtual void IncreaseScreenBrightness() OVERRIDE {
    SimpleMethodCallToPowerManager(
        power_manager::kIncreaseScreenBrightnessMethod);
  }

  virtual void DecreaseKeyboardBrightness() OVERRIDE {
    SimpleMethodCallToPowerManager(
        power_manager::kDecreaseKeyboardBrightnessMethod);
  }

  virtual void IncreaseKeyboardBrightness() OVERRIDE {
    SimpleMethodCallToPowerManager(
        power_manager::kIncreaseKeyboardBrightnessMethod);
  }

  virtual void SetScreenBrightnessPercent(double percent,
                                          bool gradual) OVERRIDE {
    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kSetScreenBrightnessPercentMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendDouble(percent);
    writer.AppendInt32(
        gradual ?
        power_manager::kBrightnessTransitionGradual :
        power_manager::kBrightnessTransitionInstant);
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        dbus::ObjectProxy::EmptyResponseCallback());
  }

  virtual void GetScreenBrightnessPercent(
      const GetScreenBrightnessPercentCallback& callback) OVERRIDE {
    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kGetScreenBrightnessPercentMethod);
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::Bind(&PowerManagerClientImpl::OnGetScreenBrightnessPercent,
                   weak_ptr_factory_.GetWeakPtr(), callback));
  }

  virtual void RequestStatusUpdate() OVERRIDE {
    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kGetPowerSupplyPropertiesMethod);
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::Bind(&PowerManagerClientImpl::OnGetPowerSupplyPropertiesMethod,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  virtual void RequestRestart() OVERRIDE {
    SimpleMethodCallToPowerManager(power_manager::kRequestRestartMethod);
  };

  virtual void RequestShutdown() OVERRIDE {
    SimpleMethodCallToPowerManager(power_manager::kRequestShutdownMethod);
  }

  virtual void NotifyUserActivity(
      power_manager::UserActivityType type) OVERRIDE {
    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kHandleUserActivityMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendInt32(type);

    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        dbus::ObjectProxy::EmptyResponseCallback());
  }

  virtual void NotifyVideoActivity(bool is_fullscreen) OVERRIDE {
    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kHandleVideoActivityMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendBool(is_fullscreen);

    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        dbus::ObjectProxy::EmptyResponseCallback());
  }

  virtual void SetPolicy(
      const power_manager::PowerManagementPolicy& policy) OVERRIDE {
    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kSetPolicyMethod);
    dbus::MessageWriter writer(&method_call);
    if (!writer.AppendProtoAsArrayOfBytes(policy)) {
      LOG(ERROR) << "Error calling " << power_manager::kSetPolicyMethod;
      return;
    }
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        dbus::ObjectProxy::EmptyResponseCallback());
  }

  virtual void SetIsProjecting(bool is_projecting) OVERRIDE {
    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kSetIsProjectingMethod);
    dbus::MessageWriter writer(&method_call);
    writer.AppendBool(is_projecting);
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        dbus::ObjectProxy::EmptyResponseCallback());
    last_is_projecting_ = is_projecting;
  }

  virtual base::Closure GetSuspendReadinessCallback() OVERRIDE {
    DCHECK(OnOriginThread());
    DCHECK(suspend_is_pending_);
    num_pending_suspend_readiness_callbacks_++;
    return base::Bind(&PowerManagerClientImpl::HandleObserverSuspendReadiness,
                      weak_ptr_factory_.GetWeakPtr(), pending_suspend_id_);
  }

  virtual int GetNumPendingSuspendReadinessCallbacks() OVERRIDE {
    return num_pending_suspend_readiness_callbacks_;
  }

 protected:
  virtual void Init(dbus::Bus* bus) OVERRIDE {
    power_manager_proxy_ = bus->GetObjectProxy(
        power_manager::kPowerManagerServiceName,
        dbus::ObjectPath(power_manager::kPowerManagerServicePath));

    power_manager_proxy_->SetNameOwnerChangedCallback(
        base::Bind(&PowerManagerClientImpl::NameOwnerChangedReceived,
                   weak_ptr_factory_.GetWeakPtr()));

    // Monitor the D-Bus signal for brightness changes. Only the power
    // manager knows the actual brightness level. We don't cache the
    // brightness level in Chrome as it'll make things less reliable.
    power_manager_proxy_->ConnectToSignal(
        power_manager::kPowerManagerInterface,
        power_manager::kBrightnessChangedSignal,
        base::Bind(&PowerManagerClientImpl::BrightnessChangedReceived,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&PowerManagerClientImpl::SignalConnected,
                   weak_ptr_factory_.GetWeakPtr()));

    power_manager_proxy_->ConnectToSignal(
        power_manager::kPowerManagerInterface,
        power_manager::kPeripheralBatteryStatusSignal,
        base::Bind(&PowerManagerClientImpl::PeripheralBatteryStatusReceived,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&PowerManagerClientImpl::SignalConnected,
                   weak_ptr_factory_.GetWeakPtr()));

    power_manager_proxy_->ConnectToSignal(
        power_manager::kPowerManagerInterface,
        power_manager::kPowerSupplyPollSignal,
        base::Bind(&PowerManagerClientImpl::PowerSupplyPollReceived,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&PowerManagerClientImpl::SignalConnected,
                   weak_ptr_factory_.GetWeakPtr()));

    power_manager_proxy_->ConnectToSignal(
        power_manager::kPowerManagerInterface,
        power_manager::kInputEventSignal,
        base::Bind(&PowerManagerClientImpl::InputEventReceived,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&PowerManagerClientImpl::SignalConnected,
                   weak_ptr_factory_.GetWeakPtr()));

    power_manager_proxy_->ConnectToSignal(
        power_manager::kPowerManagerInterface,
        power_manager::kSuspendImminentSignal,
        base::Bind(
            &PowerManagerClientImpl::SuspendImminentReceived,
            weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&PowerManagerClientImpl::SignalConnected,
                   weak_ptr_factory_.GetWeakPtr()));

    power_manager_proxy_->ConnectToSignal(
        power_manager::kPowerManagerInterface,
        power_manager::kSuspendDoneSignal,
        base::Bind(&PowerManagerClientImpl::SuspendDoneReceived,
                   weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&PowerManagerClientImpl::SignalConnected,
                   weak_ptr_factory_.GetWeakPtr()));

    power_manager_proxy_->ConnectToSignal(
        power_manager::kPowerManagerInterface,
        power_manager::kIdleActionImminentSignal,
        base::Bind(
            &PowerManagerClientImpl::IdleActionImminentReceived,
            weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&PowerManagerClientImpl::SignalConnected,
                   weak_ptr_factory_.GetWeakPtr()));

    power_manager_proxy_->ConnectToSignal(
        power_manager::kPowerManagerInterface,
        power_manager::kIdleActionDeferredSignal,
        base::Bind(
            &PowerManagerClientImpl::IdleActionDeferredReceived,
            weak_ptr_factory_.GetWeakPtr()),
        base::Bind(&PowerManagerClientImpl::SignalConnected,
                   weak_ptr_factory_.GetWeakPtr()));

    RegisterSuspendDelay();
  }

 private:
  // Returns true if the current thread is the origin thread.
  bool OnOriginThread() {
    return base::PlatformThread::CurrentId() == origin_thread_id_;
  }

  // Called when a dbus signal is initially connected.
  void SignalConnected(const std::string& interface_name,
                       const std::string& signal_name,
                       bool success) {
    LOG_IF(WARNING, !success) << "Failed to connect to signal "
                              << signal_name << ".";
  }

  // Makes a method call to power manager with no arguments and no response.
  void SimpleMethodCallToPowerManager(const std::string& method_name) {
    dbus::MethodCall method_call(power_manager::kPowerManagerInterface,
                                 method_name);
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        dbus::ObjectProxy::EmptyResponseCallback());
  }

  void NameOwnerChangedReceived(const std::string& old_owner,
                                const std::string& new_owner) {
    VLOG(1) << "Power manager restarted (old owner was "
            << (old_owner.empty() ? "[none]" : old_owner.c_str())
            << ", new owner is "
            << (new_owner.empty() ? "[none]" : new_owner.c_str()) << ")";
    if (!new_owner.empty()) {
      VLOG(1) << "Sending initial state to power manager";
      RegisterSuspendDelay();
      SetIsProjecting(last_is_projecting_);
      FOR_EACH_OBSERVER(Observer, observers_, PowerManagerRestarted());
    }
  }

  void BrightnessChangedReceived(dbus::Signal* signal) {
    dbus::MessageReader reader(signal);
    int32 brightness_level = 0;
    bool user_initiated = 0;
    if (!(reader.PopInt32(&brightness_level) &&
          reader.PopBool(&user_initiated))) {
      LOG(ERROR) << "Brightness changed signal had incorrect parameters: "
                 << signal->ToString();
      return;
    }
    VLOG(1) << "Brightness changed to " << brightness_level
            << ": user initiated " << user_initiated;
    FOR_EACH_OBSERVER(Observer, observers_,
                      BrightnessChanged(brightness_level, user_initiated));
  }

  void PeripheralBatteryStatusReceived(dbus::Signal* signal) {
    dbus::MessageReader reader(signal);
    power_manager::PeripheralBatteryStatus protobuf_status;
    if (!reader.PopArrayOfBytesAsProto(&protobuf_status)) {
      LOG(ERROR) << "Unable to decode protocol buffer from "
                 << power_manager::kPeripheralBatteryStatusSignal << " signal";
      return;
    }

    std::string path = protobuf_status.path();
    std::string name = protobuf_status.name();
    int level = protobuf_status.has_level() ? protobuf_status.level() : -1;

    VLOG(1) << "Device battery status received " << level
            << " for " << name << " at " << path;

    FOR_EACH_OBSERVER(Observer, observers_,
                      PeripheralBatteryStatusReceived(path, name, level));
  }

  void PowerSupplyPollReceived(dbus::Signal* signal) {
    VLOG(1) << "Received power supply poll signal.";
    dbus::MessageReader reader(signal);
    power_manager::PowerSupplyProperties protobuf;
    if (reader.PopArrayOfBytesAsProto(&protobuf)) {
      FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(protobuf));
    } else {
      LOG(ERROR) << "Unable to decode "
                 << power_manager::kPowerSupplyPollSignal << "signal";
    }
  }

  void OnGetPowerSupplyPropertiesMethod(dbus::Response* response) {
    if (!response) {
      LOG(ERROR) << "Error calling "
                 << power_manager::kGetPowerSupplyPropertiesMethod;
      return;
    }

    dbus::MessageReader reader(response);
    power_manager::PowerSupplyProperties protobuf;
    if (reader.PopArrayOfBytesAsProto(&protobuf)) {
      FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(protobuf));
    } else {
      LOG(ERROR) << "Unable to decode "
                 << power_manager::kGetPowerSupplyPropertiesMethod
                 << " response";
    }
  }

  void OnGetScreenBrightnessPercent(
      const GetScreenBrightnessPercentCallback& callback,
      dbus::Response* response) {
    if (!response) {
      LOG(ERROR) << "Error calling "
                 << power_manager::kGetScreenBrightnessPercentMethod;
      return;
    }
    dbus::MessageReader reader(response);
    double percent = 0.0;
    if (!reader.PopDouble(&percent))
      LOG(ERROR) << "Error reading response from powerd: "
                 << response->ToString();
    callback.Run(percent);
  }

  void OnRegisterSuspendDelayReply(dbus::Response* response) {
    if (!response) {
      LOG(ERROR) << "Error calling "
                 << power_manager::kRegisterSuspendDelayMethod;
      return;
    }

    dbus::MessageReader reader(response);
    power_manager::RegisterSuspendDelayReply protobuf;
    if (!reader.PopArrayOfBytesAsProto(&protobuf)) {
      LOG(ERROR) << "Unable to parse reply from "
                 << power_manager::kRegisterSuspendDelayMethod;
      return;
    }

    suspend_delay_id_ = protobuf.delay_id();
    has_suspend_delay_id_ = true;
    VLOG(1) << "Registered suspend delay " << suspend_delay_id_;
  }

  void SuspendImminentReceived(dbus::Signal* signal) {
    if (!has_suspend_delay_id_) {
      LOG(ERROR) << "Received unrequested "
                 << power_manager::kSuspendImminentSignal << " signal";
      return;
    }

    dbus::MessageReader reader(signal);
    power_manager::SuspendImminent proto;
    if (!reader.PopArrayOfBytesAsProto(&proto)) {
      LOG(ERROR) << "Unable to decode protocol buffer from "
                 << power_manager::kSuspendImminentSignal << " signal";
      return;
    }

    VLOG(1) << "Got " << power_manager::kSuspendImminentSignal << " signal "
            << "announcing suspend attempt " << proto.suspend_id();
    if (suspend_is_pending_) {
      LOG(WARNING) << "Got " << power_manager::kSuspendImminentSignal
                   << " signal about pending suspend attempt "
                   << proto.suspend_id() << " while still waiting "
                   << "on attempt " << pending_suspend_id_;
    }

    pending_suspend_id_ = proto.suspend_id();
    suspend_is_pending_ = true;
    num_pending_suspend_readiness_callbacks_ = 0;
    FOR_EACH_OBSERVER(Observer, observers_, SuspendImminent());
    MaybeReportSuspendReadiness();
  }

  void SuspendDoneReceived(dbus::Signal* signal) {
    dbus::MessageReader reader(signal);
    power_manager::SuspendDone proto;
    if (!reader.PopArrayOfBytesAsProto(&proto)) {
      LOG(ERROR) << "Unable to decode protocol buffer from "
                 << power_manager::kSuspendDoneSignal << " signal";
      return;
    }

    const base::TimeDelta duration =
        base::TimeDelta::FromInternalValue(proto.suspend_duration());
    VLOG(1) << "Got " << power_manager::kSuspendDoneSignal << " signal:"
            << " suspend_id=" << proto.suspend_id()
            << " duration=" << duration.InSeconds() << " sec";
    FOR_EACH_OBSERVER(
        PowerManagerClient::Observer, observers_, SuspendDone(duration));
  }

  void IdleActionImminentReceived(dbus::Signal* signal) {
    dbus::MessageReader reader(signal);
    power_manager::IdleActionImminent proto;
    if (!reader.PopArrayOfBytesAsProto(&proto)) {
      LOG(ERROR) << "Unable to decode protocol buffer from "
                 << power_manager::kIdleActionImminentSignal << " signal";
      return;
    }
    FOR_EACH_OBSERVER(Observer, observers_,
        IdleActionImminent(base::TimeDelta::FromInternalValue(
            proto.time_until_idle_action())));
  }

  void IdleActionDeferredReceived(dbus::Signal* signal) {
    FOR_EACH_OBSERVER(Observer, observers_, IdleActionDeferred());
  }

  void InputEventReceived(dbus::Signal* signal) {
    dbus::MessageReader reader(signal);
    power_manager::InputEvent proto;
    if (!reader.PopArrayOfBytesAsProto(&proto)) {
      LOG(ERROR) << "Unable to decode protocol buffer from "
                 << power_manager::kInputEventSignal << " signal";
      return;
    }

    base::TimeTicks timestamp =
        base::TimeTicks::FromInternalValue(proto.timestamp());
    VLOG(1) << "Got " << power_manager::kInputEventSignal << " signal:"
            << " type=" << proto.type() << " timestamp=" << proto.timestamp();
    switch (proto.type()) {
      case power_manager::InputEvent_Type_POWER_BUTTON_DOWN:
      case power_manager::InputEvent_Type_POWER_BUTTON_UP: {
        const bool down =
            (proto.type() == power_manager::InputEvent_Type_POWER_BUTTON_DOWN);
        FOR_EACH_OBSERVER(PowerManagerClient::Observer, observers_,
                          PowerButtonEventReceived(down, timestamp));

        // Tell powerd that Chrome has handled power button presses.
        if (down) {
          dbus::MethodCall method_call(
              power_manager::kPowerManagerInterface,
              power_manager::kHandlePowerButtonAcknowledgmentMethod);
          dbus::MessageWriter writer(&method_call);
          writer.AppendInt64(proto.timestamp());
          power_manager_proxy_->CallMethod(
              &method_call,
              dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
              dbus::ObjectProxy::EmptyResponseCallback());
        }
        break;
      }
      case power_manager::InputEvent_Type_LID_OPEN:
      case power_manager::InputEvent_Type_LID_CLOSED: {
        bool open =
            (proto.type() == power_manager::InputEvent_Type_LID_OPEN);
        FOR_EACH_OBSERVER(PowerManagerClient::Observer, observers_,
                          LidEventReceived(open, timestamp));
        break;
      }
    }
  }

  // Registers a suspend delay with the power manager.  This is usually
  // only called at startup, but if the power manager restarts, we need to
  // create a new delay.
  void RegisterSuspendDelay() {
    // Throw out any old delay that was registered.
    suspend_delay_id_ = -1;
    has_suspend_delay_id_ = false;

    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kRegisterSuspendDelayMethod);
    dbus::MessageWriter writer(&method_call);

    power_manager::RegisterSuspendDelayRequest protobuf_request;
    base::TimeDelta timeout =
        base::TimeDelta::FromMilliseconds(kSuspendDelayTimeoutMs);
    protobuf_request.set_timeout(timeout.ToInternalValue());
    protobuf_request.set_description(kSuspendDelayDescription);

    if (!writer.AppendProtoAsArrayOfBytes(protobuf_request)) {
      LOG(ERROR) << "Error constructing message for "
                 << power_manager::kRegisterSuspendDelayMethod;
      return;
    }
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        base::Bind(
            &PowerManagerClientImpl::OnRegisterSuspendDelayReply,
            weak_ptr_factory_.GetWeakPtr()));
  }

  // Records the fact that an observer has finished doing asynchronous work
  // that was blocking a pending suspend attempt and possibly reports
  // suspend readiness to powerd.  Called by callbacks returned via
  // GetSuspendReadinessCallback().
  void HandleObserverSuspendReadiness(int32 suspend_id) {
    DCHECK(OnOriginThread());
    if (!suspend_is_pending_ || suspend_id != pending_suspend_id_)
      return;

    num_pending_suspend_readiness_callbacks_--;
    MaybeReportSuspendReadiness();
  }

  // Reports suspend readiness to powerd if no observers are still holding
  // suspend readiness callbacks.
  void MaybeReportSuspendReadiness() {
    if (!suspend_is_pending_ || num_pending_suspend_readiness_callbacks_ > 0)
      return;

    dbus::MethodCall method_call(
        power_manager::kPowerManagerInterface,
        power_manager::kHandleSuspendReadinessMethod);
    dbus::MessageWriter writer(&method_call);

    VLOG(1) << "Announcing readiness of suspend delay " << suspend_delay_id_
            << " for suspend attempt " << pending_suspend_id_;
    power_manager::SuspendReadinessInfo protobuf_request;
    protobuf_request.set_delay_id(suspend_delay_id_);
    protobuf_request.set_suspend_id(pending_suspend_id_);

    pending_suspend_id_ = -1;
    suspend_is_pending_ = false;

    if (!writer.AppendProtoAsArrayOfBytes(protobuf_request)) {
      LOG(ERROR) << "Error constructing message for "
                 << power_manager::kHandleSuspendReadinessMethod;
      return;
    }
    power_manager_proxy_->CallMethod(
        &method_call,
        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
        dbus::ObjectProxy::EmptyResponseCallback());
  }

  // Origin thread (i.e. the UI thread in production).
  base::PlatformThreadId origin_thread_id_;

  dbus::ObjectProxy* power_manager_proxy_;
  ObserverList<Observer> observers_;

  // The delay_id_ obtained from the RegisterSuspendDelay request.
  int32 suspend_delay_id_;
  bool has_suspend_delay_id_;

  // powerd-supplied ID corresponding to an imminent suspend attempt that is
  // currently being delayed.
  int32 pending_suspend_id_;
  bool suspend_is_pending_;

  // Number of callbacks that have been returned by
  // GetSuspendReadinessCallback() during the currently-pending suspend
  // attempt but have not yet been called.
  int num_pending_suspend_readiness_callbacks_;

  // Last state passed to SetIsProjecting().
  bool last_is_projecting_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<PowerManagerClientImpl> weak_ptr_factory_;

  DISALLOW_COPY_AND_ASSIGN(PowerManagerClientImpl);
};

// The PowerManagerClient implementation used on Linux desktop,
// which does nothing.
class PowerManagerClientStubImpl : public PowerManagerClient {
 public:
  PowerManagerClientStubImpl()
      : discharging_(true),
        battery_percentage_(40),
        brightness_(50.0),
        pause_count_(2),
        cycle_count_(0),
        num_pending_suspend_readiness_callbacks_(0),
        weak_ptr_factory_(this) {}

  virtual ~PowerManagerClientStubImpl() {}

  int num_pending_suspend_readiness_callbacks() const {
    return num_pending_suspend_readiness_callbacks_;
  }

  // PowerManagerClient overrides:
  virtual void Init(dbus::Bus* bus) OVERRIDE {
    ParseCommandLineSwitch();
    if (power_cycle_delay_ != base::TimeDelta()) {
      update_timer_.Start(FROM_HERE,
                          power_cycle_delay_,
                          this,
                          &PowerManagerClientStubImpl::UpdateStatus);
    }
  }

  virtual void AddObserver(Observer* observer) OVERRIDE {
    observers_.AddObserver(observer);
  }

  virtual void RemoveObserver(Observer* observer) OVERRIDE {
    observers_.RemoveObserver(observer);
  }

  virtual bool HasObserver(Observer* observer) OVERRIDE {
    return observers_.HasObserver(observer);
  }

  virtual void DecreaseScreenBrightness(bool allow_off) OVERRIDE {
    VLOG(1) << "Requested to descrease screen brightness";
    SetBrightness(brightness_ - 5.0, true);
  }

  virtual void IncreaseScreenBrightness() OVERRIDE {
    VLOG(1) << "Requested to increase screen brightness";
    SetBrightness(brightness_ + 5.0, true);
  }

  virtual void SetScreenBrightnessPercent(double percent,
                                          bool gradual) OVERRIDE {
    VLOG(1) << "Requested to set screen brightness to " << percent << "% "
            << (gradual ? "gradually" : "instantaneously");
    SetBrightness(percent, false);
  }

  virtual void GetScreenBrightnessPercent(
      const GetScreenBrightnessPercentCallback& callback) OVERRIDE {
    callback.Run(brightness_);
  }

  virtual void DecreaseKeyboardBrightness() OVERRIDE {
    VLOG(1) << "Requested to descrease keyboard brightness";
  }

  virtual void IncreaseKeyboardBrightness() OVERRIDE {
    VLOG(1) << "Requested to increase keyboard brightness";
  }

  virtual void RequestStatusUpdate() OVERRIDE {
    base::MessageLoop::current()->PostTask(FROM_HERE,
        base::Bind(&PowerManagerClientStubImpl::UpdateStatus,
                   weak_ptr_factory_.GetWeakPtr()));
  }

  virtual void RequestRestart() OVERRIDE {}
  virtual void RequestShutdown() OVERRIDE {}

  virtual void NotifyUserActivity(
      power_manager::UserActivityType type) OVERRIDE {}
  virtual void NotifyVideoActivity(bool is_fullscreen) OVERRIDE {}
  virtual void SetPolicy(
      const power_manager::PowerManagementPolicy& policy) OVERRIDE {}
  virtual void SetIsProjecting(bool is_projecting) OVERRIDE {}
  virtual base::Closure GetSuspendReadinessCallback() OVERRIDE {
    num_pending_suspend_readiness_callbacks_++;
    return base::Bind(&PowerManagerClientStubImpl::HandleSuspendReadiness,
                      weak_ptr_factory_.GetWeakPtr());
  }
  virtual int GetNumPendingSuspendReadinessCallbacks() OVERRIDE {
    return num_pending_suspend_readiness_callbacks_;
  }

 private:
  void HandleSuspendReadiness() {
    num_pending_suspend_readiness_callbacks_--;
  }

  void UpdateStatus() {
    if (pause_count_ > 0) {
      pause_count_--;
      if (pause_count_ == 2)
        discharging_ = !discharging_;
    } else {
      if (discharging_)
        battery_percentage_ -= (battery_percentage_ <= 10 ? 1 : 10);
      else
        battery_percentage_ += (battery_percentage_ >= 10 ? 10 : 1);
      battery_percentage_ = std::min(std::max(battery_percentage_, 0), 100);
      // We pause at 0 and 100% so that it's easier to check those conditions.
      if (battery_percentage_ == 0 || battery_percentage_ == 100) {
        pause_count_ = 4;
        if (battery_percentage_ == 100)
          cycle_count_ = (cycle_count_ + 1) % 3;
      }
    }

    const int kSecondsToEmptyFullBattery = 3 * 60 * 60;  // 3 hours.
    int64 remaining_battery_time =
        std::max(1, battery_percentage_ * kSecondsToEmptyFullBattery / 100);

    props_.Clear();

    switch (cycle_count_) {
      case 0:
        // Say that the system is charging with AC connected and
        // discharging without any charger connected.
        props_.set_external_power(discharging_ ?
            power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED :
            power_manager::PowerSupplyProperties_ExternalPower_AC);
        break;
      case 1:
        // Say that the system is both charging and discharging on USB
        // (i.e. a low-power charger).
        props_.set_external_power(
            power_manager::PowerSupplyProperties_ExternalPower_USB);
        break;
      case 2:
        // Say that the system is both charging and discharging on AC.
        props_.set_external_power(
            power_manager::PowerSupplyProperties_ExternalPower_AC);
        break;
      default:
        NOTREACHED() << "Unhandled cycle " << cycle_count_;
    }

    if (battery_percentage_ == 100 && !discharging_) {
      props_.set_battery_state(
          power_manager::PowerSupplyProperties_BatteryState_FULL);
    } else if (!discharging_) {
      props_.set_battery_state(
          power_manager::PowerSupplyProperties_BatteryState_CHARGING);
      props_.set_battery_time_to_full_sec(std::max(static_cast<int64>(1),
          kSecondsToEmptyFullBattery - remaining_battery_time));
    } else {
      props_.set_battery_state(
          power_manager::PowerSupplyProperties_BatteryState_DISCHARGING);
      props_.set_battery_time_to_empty_sec(remaining_battery_time);
    }

    props_.set_battery_percent(battery_percentage_);
    props_.set_is_calculating_battery_time(pause_count_ > 1);

    FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(props_));
  }

  void SetBrightness(double percent, bool user_initiated) {
    brightness_ = std::min(std::max(0.0, percent), 100.0);
    int brightness_level = static_cast<int>(brightness_);
    FOR_EACH_OBSERVER(Observer, observers_,
                      BrightnessChanged(brightness_level, user_initiated));
  }

  void ParseCommandLineSwitch() {
    CommandLine* command_line = CommandLine::ForCurrentProcess();
    if (!command_line || !command_line->HasSwitch(switches::kPowerStub))
      return;
    std::string option_str =
        command_line->GetSwitchValueASCII(switches::kPowerStub);
    base::StringPairs string_pairs;
    base::SplitStringIntoKeyValuePairs(option_str, '=', ',', &string_pairs);
    for (base::StringPairs::iterator iter = string_pairs.begin();
         iter != string_pairs.end(); ++iter) {
      ParseOption((*iter).first, (*iter).second);
    }
  }

  void ParseOption(const std::string& arg0, const std::string& arg1) {
    if (arg0 == "cycle" || arg0 == "interactive") {
      int seconds = 1;
      if (!arg1.empty())
        base::StringToInt(arg1, &seconds);
      power_cycle_delay_ = base::TimeDelta::FromSeconds(seconds);
    }
  }

  base::TimeDelta power_cycle_delay_;  // Time over which to cycle power state
  bool discharging_;
  int battery_percentage_;
  double brightness_;
  int pause_count_;
  int cycle_count_;
  ObserverList<Observer> observers_;
  base::RepeatingTimer<PowerManagerClientStubImpl> update_timer_;
  power_manager::PowerSupplyProperties props_;

  // Number of callbacks returned by GetSuspendReadinessCallback() but not yet
  // invoked.
  int num_pending_suspend_readiness_callbacks_;

  // Note: This should remain the last member so it'll be destroyed and
  // invalidate its weak pointers before any other members are destroyed.
  base::WeakPtrFactory<PowerManagerClientStubImpl> weak_ptr_factory_;
};

PowerManagerClient::PowerManagerClient() {
}

PowerManagerClient::~PowerManagerClient() {
}

// static
PowerManagerClient* PowerManagerClient::Create(
    DBusClientImplementationType type) {
  if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
    return new PowerManagerClientImpl();
  DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
  return new PowerManagerClientStubImpl();
}

}  // namespace chromeos