1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chromeos/dbus/power_manager_client.h"
6
7#include <algorithm>
8
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/command_line.h"
12#include "base/format_macros.h"
13#include "base/logging.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/message_loop/message_loop.h"
16#include "base/observer_list.h"
17#include "base/strings/string_number_conversions.h"
18#include "base/strings/string_split.h"
19#include "base/strings/stringprintf.h"
20#include "base/threading/platform_thread.h"
21#include "base/time/time.h"
22#include "base/timer/timer.h"
23#include "chromeos/chromeos_switches.h"
24#include "chromeos/dbus/power_manager/input_event.pb.h"
25#include "chromeos/dbus/power_manager/peripheral_battery_status.pb.h"
26#include "chromeos/dbus/power_manager/policy.pb.h"
27#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
28#include "chromeos/dbus/power_manager/suspend.pb.h"
29#include "dbus/bus.h"
30#include "dbus/message.h"
31#include "dbus/object_path.h"
32#include "dbus/object_proxy.h"
33
34namespace chromeos {
35
36// Maximum amount of time that the power manager will wait for Chrome to
37// say that it's ready for the system to be suspended, in milliseconds.
38const int kSuspendDelayTimeoutMs = 5000;
39
40// Human-readable description of Chrome's suspend delay.
41const char kSuspendDelayDescription[] = "chrome";
42
43// The PowerManagerClient implementation used in production.
44class PowerManagerClientImpl : public PowerManagerClient {
45 public:
46  PowerManagerClientImpl()
47      : origin_thread_id_(base::PlatformThread::CurrentId()),
48        power_manager_proxy_(NULL),
49        suspend_delay_id_(-1),
50        has_suspend_delay_id_(false),
51        dark_suspend_delay_id_(-1),
52        has_dark_suspend_delay_id_(false),
53        pending_suspend_id_(-1),
54        suspend_is_pending_(false),
55        suspending_from_dark_resume_(false),
56        num_pending_suspend_readiness_callbacks_(0),
57        last_is_projecting_(false),
58        weak_ptr_factory_(this) {}
59
60  virtual ~PowerManagerClientImpl() {
61    // Here we should unregister suspend notifications from powerd,
62    // however:
63    // - The lifetime of the PowerManagerClientImpl can extend past that of
64    //   the objectproxy,
65    // - power_manager can already detect that the client is gone and
66    //   unregister our suspend delay.
67  }
68
69  // PowerManagerClient overrides:
70
71  virtual void AddObserver(Observer* observer) OVERRIDE {
72    CHECK(observer);  // http://crbug.com/119976
73    observers_.AddObserver(observer);
74  }
75
76  virtual void RemoveObserver(Observer* observer) OVERRIDE {
77    observers_.RemoveObserver(observer);
78  }
79
80  virtual bool HasObserver(Observer* observer) OVERRIDE {
81    return observers_.HasObserver(observer);
82  }
83
84  virtual void DecreaseScreenBrightness(bool allow_off) OVERRIDE {
85    dbus::MethodCall method_call(
86        power_manager::kPowerManagerInterface,
87        power_manager::kDecreaseScreenBrightnessMethod);
88    dbus::MessageWriter writer(&method_call);
89    writer.AppendBool(allow_off);
90    power_manager_proxy_->CallMethod(
91        &method_call,
92        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
93        dbus::ObjectProxy::EmptyResponseCallback());
94  }
95
96  virtual void IncreaseScreenBrightness() OVERRIDE {
97    SimpleMethodCallToPowerManager(
98        power_manager::kIncreaseScreenBrightnessMethod);
99  }
100
101  virtual void DecreaseKeyboardBrightness() OVERRIDE {
102    SimpleMethodCallToPowerManager(
103        power_manager::kDecreaseKeyboardBrightnessMethod);
104  }
105
106  virtual void IncreaseKeyboardBrightness() OVERRIDE {
107    SimpleMethodCallToPowerManager(
108        power_manager::kIncreaseKeyboardBrightnessMethod);
109  }
110
111  virtual void SetScreenBrightnessPercent(double percent,
112                                          bool gradual) OVERRIDE {
113    dbus::MethodCall method_call(
114        power_manager::kPowerManagerInterface,
115        power_manager::kSetScreenBrightnessPercentMethod);
116    dbus::MessageWriter writer(&method_call);
117    writer.AppendDouble(percent);
118    writer.AppendInt32(
119        gradual ?
120        power_manager::kBrightnessTransitionGradual :
121        power_manager::kBrightnessTransitionInstant);
122    power_manager_proxy_->CallMethod(
123        &method_call,
124        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
125        dbus::ObjectProxy::EmptyResponseCallback());
126  }
127
128  virtual void GetScreenBrightnessPercent(
129      const GetScreenBrightnessPercentCallback& callback) OVERRIDE {
130    dbus::MethodCall method_call(
131        power_manager::kPowerManagerInterface,
132        power_manager::kGetScreenBrightnessPercentMethod);
133    power_manager_proxy_->CallMethod(
134        &method_call,
135        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
136        base::Bind(&PowerManagerClientImpl::OnGetScreenBrightnessPercent,
137                   weak_ptr_factory_.GetWeakPtr(), callback));
138  }
139
140  virtual void RequestStatusUpdate() OVERRIDE {
141    dbus::MethodCall method_call(
142        power_manager::kPowerManagerInterface,
143        power_manager::kGetPowerSupplyPropertiesMethod);
144    power_manager_proxy_->CallMethod(
145        &method_call,
146        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
147        base::Bind(&PowerManagerClientImpl::OnGetPowerSupplyPropertiesMethod,
148                   weak_ptr_factory_.GetWeakPtr()));
149  }
150
151  virtual void RequestSuspend() OVERRIDE {
152    SimpleMethodCallToPowerManager(power_manager::kRequestSuspendMethod);
153  }
154
155  virtual void RequestRestart() OVERRIDE {
156    SimpleMethodCallToPowerManager(power_manager::kRequestRestartMethod);
157  }
158
159  virtual void RequestShutdown() OVERRIDE {
160    SimpleMethodCallToPowerManager(power_manager::kRequestShutdownMethod);
161  }
162
163  virtual void NotifyUserActivity(
164      power_manager::UserActivityType type) OVERRIDE {
165    dbus::MethodCall method_call(
166        power_manager::kPowerManagerInterface,
167        power_manager::kHandleUserActivityMethod);
168    dbus::MessageWriter writer(&method_call);
169    writer.AppendInt32(type);
170
171    power_manager_proxy_->CallMethod(
172        &method_call,
173        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
174        dbus::ObjectProxy::EmptyResponseCallback());
175  }
176
177  virtual void NotifyVideoActivity(bool is_fullscreen) OVERRIDE {
178    dbus::MethodCall method_call(
179        power_manager::kPowerManagerInterface,
180        power_manager::kHandleVideoActivityMethod);
181    dbus::MessageWriter writer(&method_call);
182    writer.AppendBool(is_fullscreen);
183
184    power_manager_proxy_->CallMethod(
185        &method_call,
186        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
187        dbus::ObjectProxy::EmptyResponseCallback());
188  }
189
190  virtual void SetPolicy(
191      const power_manager::PowerManagementPolicy& policy) OVERRIDE {
192    dbus::MethodCall method_call(
193        power_manager::kPowerManagerInterface,
194        power_manager::kSetPolicyMethod);
195    dbus::MessageWriter writer(&method_call);
196    if (!writer.AppendProtoAsArrayOfBytes(policy)) {
197      LOG(ERROR) << "Error calling " << power_manager::kSetPolicyMethod;
198      return;
199    }
200    power_manager_proxy_->CallMethod(
201        &method_call,
202        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
203        dbus::ObjectProxy::EmptyResponseCallback());
204  }
205
206  virtual void SetIsProjecting(bool is_projecting) OVERRIDE {
207    dbus::MethodCall method_call(
208        power_manager::kPowerManagerInterface,
209        power_manager::kSetIsProjectingMethod);
210    dbus::MessageWriter writer(&method_call);
211    writer.AppendBool(is_projecting);
212    power_manager_proxy_->CallMethod(
213        &method_call,
214        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
215        dbus::ObjectProxy::EmptyResponseCallback());
216    last_is_projecting_ = is_projecting;
217  }
218
219  virtual base::Closure GetSuspendReadinessCallback() OVERRIDE {
220    DCHECK(OnOriginThread());
221    DCHECK(suspend_is_pending_);
222    num_pending_suspend_readiness_callbacks_++;
223    return base::Bind(&PowerManagerClientImpl::HandleObserverSuspendReadiness,
224                      weak_ptr_factory_.GetWeakPtr(), pending_suspend_id_,
225                      suspending_from_dark_resume_);
226  }
227
228  virtual int GetNumPendingSuspendReadinessCallbacks() OVERRIDE {
229    return num_pending_suspend_readiness_callbacks_;
230  }
231
232 protected:
233  virtual void Init(dbus::Bus* bus) OVERRIDE {
234    power_manager_proxy_ = bus->GetObjectProxy(
235        power_manager::kPowerManagerServiceName,
236        dbus::ObjectPath(power_manager::kPowerManagerServicePath));
237
238    power_manager_proxy_->SetNameOwnerChangedCallback(
239        base::Bind(&PowerManagerClientImpl::NameOwnerChangedReceived,
240                   weak_ptr_factory_.GetWeakPtr()));
241
242    // Monitor the D-Bus signal for brightness changes. Only the power
243    // manager knows the actual brightness level. We don't cache the
244    // brightness level in Chrome as it'll make things less reliable.
245    power_manager_proxy_->ConnectToSignal(
246        power_manager::kPowerManagerInterface,
247        power_manager::kBrightnessChangedSignal,
248        base::Bind(&PowerManagerClientImpl::BrightnessChangedReceived,
249                   weak_ptr_factory_.GetWeakPtr()),
250        base::Bind(&PowerManagerClientImpl::SignalConnected,
251                   weak_ptr_factory_.GetWeakPtr()));
252
253    power_manager_proxy_->ConnectToSignal(
254        power_manager::kPowerManagerInterface,
255        power_manager::kPeripheralBatteryStatusSignal,
256        base::Bind(&PowerManagerClientImpl::PeripheralBatteryStatusReceived,
257                   weak_ptr_factory_.GetWeakPtr()),
258        base::Bind(&PowerManagerClientImpl::SignalConnected,
259                   weak_ptr_factory_.GetWeakPtr()));
260
261    power_manager_proxy_->ConnectToSignal(
262        power_manager::kPowerManagerInterface,
263        power_manager::kPowerSupplyPollSignal,
264        base::Bind(&PowerManagerClientImpl::PowerSupplyPollReceived,
265                   weak_ptr_factory_.GetWeakPtr()),
266        base::Bind(&PowerManagerClientImpl::SignalConnected,
267                   weak_ptr_factory_.GetWeakPtr()));
268
269    power_manager_proxy_->ConnectToSignal(
270        power_manager::kPowerManagerInterface,
271        power_manager::kInputEventSignal,
272        base::Bind(&PowerManagerClientImpl::InputEventReceived,
273                   weak_ptr_factory_.GetWeakPtr()),
274        base::Bind(&PowerManagerClientImpl::SignalConnected,
275                   weak_ptr_factory_.GetWeakPtr()));
276
277    power_manager_proxy_->ConnectToSignal(
278        power_manager::kPowerManagerInterface,
279        power_manager::kSuspendImminentSignal,
280        base::Bind(
281            &PowerManagerClientImpl::HandleSuspendImminent,
282            weak_ptr_factory_.GetWeakPtr(), false),
283        base::Bind(&PowerManagerClientImpl::SignalConnected,
284                   weak_ptr_factory_.GetWeakPtr()));
285
286    power_manager_proxy_->ConnectToSignal(
287        power_manager::kPowerManagerInterface,
288        power_manager::kSuspendDoneSignal,
289        base::Bind(&PowerManagerClientImpl::SuspendDoneReceived,
290                   weak_ptr_factory_.GetWeakPtr()),
291        base::Bind(&PowerManagerClientImpl::SignalConnected,
292                   weak_ptr_factory_.GetWeakPtr()));
293
294    power_manager_proxy_->ConnectToSignal(
295        power_manager::kPowerManagerInterface,
296        power_manager::kDarkSuspendImminentSignal,
297        base::Bind(
298            &PowerManagerClientImpl::HandleSuspendImminent,
299            weak_ptr_factory_.GetWeakPtr(), true),
300        base::Bind(&PowerManagerClientImpl::SignalConnected,
301                   weak_ptr_factory_.GetWeakPtr()));
302
303    power_manager_proxy_->ConnectToSignal(
304        power_manager::kPowerManagerInterface,
305        power_manager::kIdleActionImminentSignal,
306        base::Bind(
307            &PowerManagerClientImpl::IdleActionImminentReceived,
308            weak_ptr_factory_.GetWeakPtr()),
309        base::Bind(&PowerManagerClientImpl::SignalConnected,
310                   weak_ptr_factory_.GetWeakPtr()));
311
312    power_manager_proxy_->ConnectToSignal(
313        power_manager::kPowerManagerInterface,
314        power_manager::kIdleActionDeferredSignal,
315        base::Bind(
316            &PowerManagerClientImpl::IdleActionDeferredReceived,
317            weak_ptr_factory_.GetWeakPtr()),
318        base::Bind(&PowerManagerClientImpl::SignalConnected,
319                   weak_ptr_factory_.GetWeakPtr()));
320
321    RegisterSuspendDelays();
322  }
323
324 private:
325  // Returns true if the current thread is the origin thread.
326  bool OnOriginThread() {
327    return base::PlatformThread::CurrentId() == origin_thread_id_;
328  }
329
330  // Called when a dbus signal is initially connected.
331  void SignalConnected(const std::string& interface_name,
332                       const std::string& signal_name,
333                       bool success) {
334    LOG_IF(WARNING, !success) << "Failed to connect to signal "
335                              << signal_name << ".";
336  }
337
338  // Makes a method call to power manager with no arguments and no response.
339  void SimpleMethodCallToPowerManager(const std::string& method_name) {
340    dbus::MethodCall method_call(power_manager::kPowerManagerInterface,
341                                 method_name);
342    power_manager_proxy_->CallMethod(
343        &method_call,
344        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
345        dbus::ObjectProxy::EmptyResponseCallback());
346  }
347
348  void NameOwnerChangedReceived(const std::string& old_owner,
349                                const std::string& new_owner) {
350    VLOG(1) << "Power manager restarted (old owner was "
351            << (old_owner.empty() ? "[none]" : old_owner.c_str())
352            << ", new owner is "
353            << (new_owner.empty() ? "[none]" : new_owner.c_str()) << ")";
354    suspend_is_pending_ = false;
355    pending_suspend_id_ = -1;
356    suspending_from_dark_resume_ = false;
357    if (!new_owner.empty()) {
358      VLOG(1) << "Sending initial state to power manager";
359      RegisterSuspendDelays();
360      SetIsProjecting(last_is_projecting_);
361      FOR_EACH_OBSERVER(Observer, observers_, PowerManagerRestarted());
362    }
363  }
364
365  void BrightnessChangedReceived(dbus::Signal* signal) {
366    dbus::MessageReader reader(signal);
367    int32_t brightness_level = 0;
368    bool user_initiated = 0;
369    if (!(reader.PopInt32(&brightness_level) &&
370          reader.PopBool(&user_initiated))) {
371      LOG(ERROR) << "Brightness changed signal had incorrect parameters: "
372                 << signal->ToString();
373      return;
374    }
375    VLOG(1) << "Brightness changed to " << brightness_level
376            << ": user initiated " << user_initiated;
377    FOR_EACH_OBSERVER(Observer, observers_,
378                      BrightnessChanged(brightness_level, user_initiated));
379  }
380
381  void PeripheralBatteryStatusReceived(dbus::Signal* signal) {
382    dbus::MessageReader reader(signal);
383    power_manager::PeripheralBatteryStatus protobuf_status;
384    if (!reader.PopArrayOfBytesAsProto(&protobuf_status)) {
385      LOG(ERROR) << "Unable to decode protocol buffer from "
386                 << power_manager::kPeripheralBatteryStatusSignal << " signal";
387      return;
388    }
389
390    std::string path = protobuf_status.path();
391    std::string name = protobuf_status.name();
392    int level = protobuf_status.has_level() ? protobuf_status.level() : -1;
393
394    VLOG(1) << "Device battery status received " << level
395            << " for " << name << " at " << path;
396
397    FOR_EACH_OBSERVER(Observer, observers_,
398                      PeripheralBatteryStatusReceived(path, name, level));
399  }
400
401  void PowerSupplyPollReceived(dbus::Signal* signal) {
402    VLOG(1) << "Received power supply poll signal.";
403    dbus::MessageReader reader(signal);
404    power_manager::PowerSupplyProperties protobuf;
405    if (reader.PopArrayOfBytesAsProto(&protobuf)) {
406      FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(protobuf));
407    } else {
408      LOG(ERROR) << "Unable to decode "
409                 << power_manager::kPowerSupplyPollSignal << "signal";
410    }
411  }
412
413  void OnGetPowerSupplyPropertiesMethod(dbus::Response* response) {
414    if (!response) {
415      LOG(ERROR) << "Error calling "
416                 << power_manager::kGetPowerSupplyPropertiesMethod;
417      return;
418    }
419
420    dbus::MessageReader reader(response);
421    power_manager::PowerSupplyProperties protobuf;
422    if (reader.PopArrayOfBytesAsProto(&protobuf)) {
423      FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(protobuf));
424    } else {
425      LOG(ERROR) << "Unable to decode "
426                 << power_manager::kGetPowerSupplyPropertiesMethod
427                 << " response";
428    }
429  }
430
431  void OnGetScreenBrightnessPercent(
432      const GetScreenBrightnessPercentCallback& callback,
433      dbus::Response* response) {
434    if (!response) {
435      LOG(ERROR) << "Error calling "
436                 << power_manager::kGetScreenBrightnessPercentMethod;
437      return;
438    }
439    dbus::MessageReader reader(response);
440    double percent = 0.0;
441    if (!reader.PopDouble(&percent))
442      LOG(ERROR) << "Error reading response from powerd: "
443                 << response->ToString();
444    callback.Run(percent);
445  }
446
447  void HandleRegisterSuspendDelayReply(bool dark_suspend,
448                                       const std::string& method_name,
449                                       dbus::Response* response) {
450    if (!response) {
451      LOG(ERROR) << "Error calling " << method_name;
452      return;
453    }
454
455    dbus::MessageReader reader(response);
456    power_manager::RegisterSuspendDelayReply protobuf;
457    if (!reader.PopArrayOfBytesAsProto(&protobuf)) {
458      LOG(ERROR) << "Unable to parse reply from " << method_name;
459      return;
460    }
461
462    if (dark_suspend) {
463      dark_suspend_delay_id_ = protobuf.delay_id();
464      has_dark_suspend_delay_id_ = true;
465      VLOG(1) << "Registered dark suspend delay " << dark_suspend_delay_id_;
466    } else {
467      suspend_delay_id_ = protobuf.delay_id();
468      has_suspend_delay_id_ = true;
469      VLOG(1) << "Registered suspend delay " << suspend_delay_id_;
470    }
471  }
472
473  void HandleSuspendImminent(bool in_dark_resume, dbus::Signal* signal) {
474    std::string signal_name = signal->GetMember();
475    if ((in_dark_resume && !has_dark_suspend_delay_id_) ||
476        (!in_dark_resume && !has_suspend_delay_id_)) {
477      LOG(ERROR) << "Received unrequested " << signal_name << " signal";
478      return;
479    }
480
481    dbus::MessageReader reader(signal);
482    power_manager::SuspendImminent proto;
483    if (!reader.PopArrayOfBytesAsProto(&proto)) {
484      LOG(ERROR) << "Unable to decode protocol buffer from " << signal_name
485                 << " signal";
486      return;
487    }
488
489    VLOG(1) << "Got " << signal_name << " signal announcing suspend attempt "
490            << proto.suspend_id();
491
492    // If a previous suspend is pending from the same state we are currently in
493    // (fully powered on or in dark resume), then something's gone a little
494    // wonky.
495    if (suspend_is_pending_ &&
496        suspending_from_dark_resume_ == in_dark_resume) {
497      LOG(WARNING) << "Got " << signal_name << " signal about pending suspend "
498                   << "attempt " << proto.suspend_id() << " while still "
499                   << "waiting on attempt " << pending_suspend_id_;
500    }
501
502    pending_suspend_id_ = proto.suspend_id();
503    suspend_is_pending_ = true;
504    suspending_from_dark_resume_ = in_dark_resume;
505    num_pending_suspend_readiness_callbacks_ = 0;
506    if (suspending_from_dark_resume_)
507      FOR_EACH_OBSERVER(Observer, observers_, DarkSuspendImminent());
508    else
509      FOR_EACH_OBSERVER(Observer, observers_, SuspendImminent());
510    MaybeReportSuspendReadiness();
511  }
512
513  void SuspendDoneReceived(dbus::Signal* signal) {
514    dbus::MessageReader reader(signal);
515    power_manager::SuspendDone proto;
516    if (!reader.PopArrayOfBytesAsProto(&proto)) {
517      LOG(ERROR) << "Unable to decode protocol buffer from "
518                 << power_manager::kSuspendDoneSignal << " signal";
519      return;
520    }
521
522    const base::TimeDelta duration =
523        base::TimeDelta::FromInternalValue(proto.suspend_duration());
524    VLOG(1) << "Got " << power_manager::kSuspendDoneSignal << " signal:"
525            << " suspend_id=" << proto.suspend_id()
526            << " duration=" << duration.InSeconds() << " sec";
527    FOR_EACH_OBSERVER(
528        PowerManagerClient::Observer, observers_, SuspendDone(duration));
529  }
530
531  void IdleActionImminentReceived(dbus::Signal* signal) {
532    dbus::MessageReader reader(signal);
533    power_manager::IdleActionImminent proto;
534    if (!reader.PopArrayOfBytesAsProto(&proto)) {
535      LOG(ERROR) << "Unable to decode protocol buffer from "
536                 << power_manager::kIdleActionImminentSignal << " signal";
537      return;
538    }
539    FOR_EACH_OBSERVER(Observer, observers_,
540        IdleActionImminent(base::TimeDelta::FromInternalValue(
541            proto.time_until_idle_action())));
542  }
543
544  void IdleActionDeferredReceived(dbus::Signal* signal) {
545    FOR_EACH_OBSERVER(Observer, observers_, IdleActionDeferred());
546  }
547
548  void InputEventReceived(dbus::Signal* signal) {
549    dbus::MessageReader reader(signal);
550    power_manager::InputEvent proto;
551    if (!reader.PopArrayOfBytesAsProto(&proto)) {
552      LOG(ERROR) << "Unable to decode protocol buffer from "
553                 << power_manager::kInputEventSignal << " signal";
554      return;
555    }
556
557    base::TimeTicks timestamp =
558        base::TimeTicks::FromInternalValue(proto.timestamp());
559    VLOG(1) << "Got " << power_manager::kInputEventSignal << " signal:"
560            << " type=" << proto.type() << " timestamp=" << proto.timestamp();
561    switch (proto.type()) {
562      case power_manager::InputEvent_Type_POWER_BUTTON_DOWN:
563      case power_manager::InputEvent_Type_POWER_BUTTON_UP: {
564        const bool down =
565            (proto.type() == power_manager::InputEvent_Type_POWER_BUTTON_DOWN);
566        FOR_EACH_OBSERVER(PowerManagerClient::Observer, observers_,
567                          PowerButtonEventReceived(down, timestamp));
568
569        // Tell powerd that Chrome has handled power button presses.
570        if (down) {
571          dbus::MethodCall method_call(
572              power_manager::kPowerManagerInterface,
573              power_manager::kHandlePowerButtonAcknowledgmentMethod);
574          dbus::MessageWriter writer(&method_call);
575          writer.AppendInt64(proto.timestamp());
576          power_manager_proxy_->CallMethod(
577              &method_call,
578              dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
579              dbus::ObjectProxy::EmptyResponseCallback());
580        }
581        break;
582      }
583      case power_manager::InputEvent_Type_LID_OPEN:
584      case power_manager::InputEvent_Type_LID_CLOSED: {
585        bool open =
586            (proto.type() == power_manager::InputEvent_Type_LID_OPEN);
587        FOR_EACH_OBSERVER(PowerManagerClient::Observer, observers_,
588                          LidEventReceived(open, timestamp));
589        break;
590      }
591    }
592  }
593
594  void RegisterSuspendDelayImpl(
595      const std::string& method_name,
596      const power_manager::RegisterSuspendDelayRequest& protobuf_request,
597      dbus::ObjectProxy::ResponseCallback callback) {
598    dbus::MethodCall method_call(
599        power_manager::kPowerManagerInterface, method_name);
600    dbus::MessageWriter writer(&method_call);
601
602    if (!writer.AppendProtoAsArrayOfBytes(protobuf_request)) {
603      LOG(ERROR) << "Error constructing message for " << method_name;
604      return;
605    }
606
607    power_manager_proxy_->CallMethod(
608        &method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT, callback);
609  }
610
611  // Registers suspend delays with the power manager.  This is usually only
612  // called at startup, but if the power manager restarts, we need to create new
613  // delays.
614  void RegisterSuspendDelays() {
615    // Throw out any old delay that was registered.
616    suspend_delay_id_ = -1;
617    has_suspend_delay_id_ = false;
618    dark_suspend_delay_id_ = -1;
619    has_dark_suspend_delay_id_ = false;
620
621    power_manager::RegisterSuspendDelayRequest protobuf_request;
622    base::TimeDelta timeout =
623        base::TimeDelta::FromMilliseconds(kSuspendDelayTimeoutMs);
624    protobuf_request.set_timeout(timeout.ToInternalValue());
625    protobuf_request.set_description(kSuspendDelayDescription);
626
627    RegisterSuspendDelayImpl(
628        power_manager::kRegisterSuspendDelayMethod,
629        protobuf_request,
630        base::Bind(&PowerManagerClientImpl::HandleRegisterSuspendDelayReply,
631                   weak_ptr_factory_.GetWeakPtr(), false,
632                   power_manager::kRegisterSuspendDelayMethod));
633    RegisterSuspendDelayImpl(
634        power_manager::kRegisterDarkSuspendDelayMethod,
635        protobuf_request,
636        base::Bind(&PowerManagerClientImpl::HandleRegisterSuspendDelayReply,
637                   weak_ptr_factory_.GetWeakPtr(), true,
638                   power_manager::kRegisterDarkSuspendDelayMethod));
639  }
640
641  // Records the fact that an observer has finished doing asynchronous work
642  // that was blocking a pending suspend attempt and possibly reports
643  // suspend readiness to powerd.  Called by callbacks returned via
644  // GetSuspendReadinessCallback().
645  void HandleObserverSuspendReadiness(int32_t suspend_id, bool in_dark_resume) {
646    DCHECK(OnOriginThread());
647    if (!suspend_is_pending_ || suspend_id != pending_suspend_id_ ||
648        in_dark_resume != suspending_from_dark_resume_)
649      return;
650
651    num_pending_suspend_readiness_callbacks_--;
652    MaybeReportSuspendReadiness();
653  }
654
655  // Reports suspend readiness to powerd if no observers are still holding
656  // suspend readiness callbacks.
657  void MaybeReportSuspendReadiness() {
658    if (!suspend_is_pending_ || num_pending_suspend_readiness_callbacks_ > 0)
659      return;
660
661    std::string method_name;
662    int32_t delay_id = -1;
663    if (suspending_from_dark_resume_) {
664      method_name = power_manager::kHandleDarkSuspendReadinessMethod;
665      delay_id = dark_suspend_delay_id_;
666    } else {
667      method_name = power_manager::kHandleSuspendReadinessMethod;
668      delay_id = suspend_delay_id_;
669    }
670
671    dbus::MethodCall method_call(
672        power_manager::kPowerManagerInterface, method_name);
673    dbus::MessageWriter writer(&method_call);
674
675    VLOG(1) << "Announcing readiness of suspend delay " << delay_id
676            << " for suspend attempt " << pending_suspend_id_;
677    power_manager::SuspendReadinessInfo protobuf_request;
678    protobuf_request.set_delay_id(delay_id);
679    protobuf_request.set_suspend_id(pending_suspend_id_);
680
681    pending_suspend_id_ = -1;
682    suspend_is_pending_ = false;
683
684    if (!writer.AppendProtoAsArrayOfBytes(protobuf_request)) {
685      LOG(ERROR) << "Error constructing message for " << method_name;
686      return;
687    }
688    power_manager_proxy_->CallMethod(
689        &method_call,
690        dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
691        dbus::ObjectProxy::EmptyResponseCallback());
692  }
693
694  // Origin thread (i.e. the UI thread in production).
695  base::PlatformThreadId origin_thread_id_;
696
697  dbus::ObjectProxy* power_manager_proxy_;
698  ObserverList<Observer> observers_;
699
700  // The delay_id_ obtained from the RegisterSuspendDelay request.
701  int32_t suspend_delay_id_;
702  bool has_suspend_delay_id_;
703
704  // The delay_id_ obtained from the RegisterDarkSuspendDelay request.
705  int32_t dark_suspend_delay_id_;
706  bool has_dark_suspend_delay_id_;
707
708  // powerd-supplied ID corresponding to an imminent suspend attempt that is
709  // currently being delayed.
710  int32_t pending_suspend_id_;
711  bool suspend_is_pending_;
712
713  // Set to true when the suspend currently being delayed was triggered during a
714  // dark resume.  Since |pending_suspend_id_| and |suspend_is_pending_| are
715  // both shared by normal and dark suspends, |suspending_from_dark_resume_|
716  // helps distinguish the context within which these variables are being used.
717  bool suspending_from_dark_resume_;
718
719  // Number of callbacks that have been returned by
720  // GetSuspendReadinessCallback() during the currently-pending suspend
721  // attempt but have not yet been called.
722  int num_pending_suspend_readiness_callbacks_;
723
724  // Last state passed to SetIsProjecting().
725  bool last_is_projecting_;
726
727  // Note: This should remain the last member so it'll be destroyed and
728  // invalidate its weak pointers before any other members are destroyed.
729  base::WeakPtrFactory<PowerManagerClientImpl> weak_ptr_factory_;
730
731  DISALLOW_COPY_AND_ASSIGN(PowerManagerClientImpl);
732};
733
734// The PowerManagerClient implementation used on Linux desktop,
735// which does nothing.
736class PowerManagerClientStubImpl : public PowerManagerClient {
737 public:
738  PowerManagerClientStubImpl()
739      : discharging_(true),
740        battery_percentage_(40),
741        brightness_(50.0),
742        pause_count_(2),
743        cycle_count_(0),
744        num_pending_suspend_readiness_callbacks_(0),
745        weak_ptr_factory_(this) {}
746
747  virtual ~PowerManagerClientStubImpl() {}
748
749  int num_pending_suspend_readiness_callbacks() const {
750    return num_pending_suspend_readiness_callbacks_;
751  }
752
753  // PowerManagerClient overrides:
754  virtual void Init(dbus::Bus* bus) OVERRIDE {
755    ParseCommandLineSwitch();
756    if (power_cycle_delay_ != base::TimeDelta()) {
757      update_timer_.Start(FROM_HERE,
758                          power_cycle_delay_,
759                          this,
760                          &PowerManagerClientStubImpl::UpdateStatus);
761    }
762  }
763
764  virtual void AddObserver(Observer* observer) OVERRIDE {
765    observers_.AddObserver(observer);
766  }
767
768  virtual void RemoveObserver(Observer* observer) OVERRIDE {
769    observers_.RemoveObserver(observer);
770  }
771
772  virtual bool HasObserver(Observer* observer) OVERRIDE {
773    return observers_.HasObserver(observer);
774  }
775
776  virtual void DecreaseScreenBrightness(bool allow_off) OVERRIDE {
777    VLOG(1) << "Requested to descrease screen brightness";
778    SetBrightness(brightness_ - 5.0, true);
779  }
780
781  virtual void IncreaseScreenBrightness() OVERRIDE {
782    VLOG(1) << "Requested to increase screen brightness";
783    SetBrightness(brightness_ + 5.0, true);
784  }
785
786  virtual void SetScreenBrightnessPercent(double percent,
787                                          bool gradual) OVERRIDE {
788    VLOG(1) << "Requested to set screen brightness to " << percent << "% "
789            << (gradual ? "gradually" : "instantaneously");
790    SetBrightness(percent, false);
791  }
792
793  virtual void GetScreenBrightnessPercent(
794      const GetScreenBrightnessPercentCallback& callback) OVERRIDE {
795    callback.Run(brightness_);
796  }
797
798  virtual void DecreaseKeyboardBrightness() OVERRIDE {
799    VLOG(1) << "Requested to descrease keyboard brightness";
800  }
801
802  virtual void IncreaseKeyboardBrightness() OVERRIDE {
803    VLOG(1) << "Requested to increase keyboard brightness";
804  }
805
806  virtual void RequestStatusUpdate() OVERRIDE {
807    base::MessageLoop::current()->PostTask(FROM_HERE,
808        base::Bind(&PowerManagerClientStubImpl::UpdateStatus,
809                   weak_ptr_factory_.GetWeakPtr()));
810  }
811
812  virtual void RequestSuspend() OVERRIDE {}
813  virtual void RequestRestart() OVERRIDE {}
814  virtual void RequestShutdown() OVERRIDE {}
815
816  virtual void NotifyUserActivity(
817      power_manager::UserActivityType type) OVERRIDE {}
818  virtual void NotifyVideoActivity(bool is_fullscreen) OVERRIDE {}
819  virtual void SetPolicy(
820      const power_manager::PowerManagementPolicy& policy) OVERRIDE {}
821  virtual void SetIsProjecting(bool is_projecting) OVERRIDE {}
822  virtual base::Closure GetSuspendReadinessCallback() OVERRIDE {
823    num_pending_suspend_readiness_callbacks_++;
824    return base::Bind(&PowerManagerClientStubImpl::HandleSuspendReadiness,
825                      weak_ptr_factory_.GetWeakPtr());
826  }
827  virtual int GetNumPendingSuspendReadinessCallbacks() OVERRIDE {
828    return num_pending_suspend_readiness_callbacks_;
829  }
830
831 private:
832  void HandleSuspendReadiness() {
833    num_pending_suspend_readiness_callbacks_--;
834  }
835
836  void UpdateStatus() {
837    if (pause_count_ > 0) {
838      pause_count_--;
839      if (pause_count_ == 2)
840        discharging_ = !discharging_;
841    } else {
842      if (discharging_)
843        battery_percentage_ -= (battery_percentage_ <= 10 ? 1 : 10);
844      else
845        battery_percentage_ += (battery_percentage_ >= 10 ? 10 : 1);
846      battery_percentage_ = std::min(std::max(battery_percentage_, 0), 100);
847      // We pause at 0 and 100% so that it's easier to check those conditions.
848      if (battery_percentage_ == 0 || battery_percentage_ == 100) {
849        pause_count_ = 4;
850        if (battery_percentage_ == 100)
851          cycle_count_ = (cycle_count_ + 1) % 3;
852      }
853    }
854
855    const int kSecondsToEmptyFullBattery = 3 * 60 * 60;  // 3 hours.
856    int64 remaining_battery_time =
857        std::max(1, battery_percentage_ * kSecondsToEmptyFullBattery / 100);
858
859    props_.Clear();
860
861    switch (cycle_count_) {
862      case 0:
863        // Say that the system is charging with AC connected and
864        // discharging without any charger connected.
865        props_.set_external_power(discharging_ ?
866            power_manager::PowerSupplyProperties_ExternalPower_DISCONNECTED :
867            power_manager::PowerSupplyProperties_ExternalPower_AC);
868        break;
869      case 1:
870        // Say that the system is both charging and discharging on USB
871        // (i.e. a low-power charger).
872        props_.set_external_power(
873            power_manager::PowerSupplyProperties_ExternalPower_USB);
874        break;
875      case 2:
876        // Say that the system is both charging and discharging on AC.
877        props_.set_external_power(
878            power_manager::PowerSupplyProperties_ExternalPower_AC);
879        break;
880      default:
881        NOTREACHED() << "Unhandled cycle " << cycle_count_;
882    }
883
884    if (battery_percentage_ == 100 && !discharging_) {
885      props_.set_battery_state(
886          power_manager::PowerSupplyProperties_BatteryState_FULL);
887    } else if (!discharging_) {
888      props_.set_battery_state(
889          power_manager::PowerSupplyProperties_BatteryState_CHARGING);
890      props_.set_battery_time_to_full_sec(std::max(static_cast<int64>(1),
891          kSecondsToEmptyFullBattery - remaining_battery_time));
892    } else {
893      props_.set_battery_state(
894          power_manager::PowerSupplyProperties_BatteryState_DISCHARGING);
895      props_.set_battery_time_to_empty_sec(remaining_battery_time);
896    }
897
898    props_.set_battery_percent(battery_percentage_);
899    props_.set_is_calculating_battery_time(pause_count_ > 1);
900
901    FOR_EACH_OBSERVER(Observer, observers_, PowerChanged(props_));
902  }
903
904  void SetBrightness(double percent, bool user_initiated) {
905    brightness_ = std::min(std::max(0.0, percent), 100.0);
906    int brightness_level = static_cast<int>(brightness_);
907    FOR_EACH_OBSERVER(Observer, observers_,
908                      BrightnessChanged(brightness_level, user_initiated));
909  }
910
911  void ParseCommandLineSwitch() {
912    CommandLine* command_line = CommandLine::ForCurrentProcess();
913    if (!command_line || !command_line->HasSwitch(switches::kPowerStub))
914      return;
915    std::string option_str =
916        command_line->GetSwitchValueASCII(switches::kPowerStub);
917    base::StringPairs string_pairs;
918    base::SplitStringIntoKeyValuePairs(option_str, '=', ',', &string_pairs);
919    for (base::StringPairs::iterator iter = string_pairs.begin();
920         iter != string_pairs.end(); ++iter) {
921      ParseOption((*iter).first, (*iter).second);
922    }
923  }
924
925  void ParseOption(const std::string& arg0, const std::string& arg1) {
926    if (arg0 == "cycle" || arg0 == "interactive") {
927      int seconds = 1;
928      if (!arg1.empty())
929        base::StringToInt(arg1, &seconds);
930      power_cycle_delay_ = base::TimeDelta::FromSeconds(seconds);
931    }
932  }
933
934  base::TimeDelta power_cycle_delay_;  // Time over which to cycle power state
935  bool discharging_;
936  int battery_percentage_;
937  double brightness_;
938  int pause_count_;
939  int cycle_count_;
940  ObserverList<Observer> observers_;
941  base::RepeatingTimer<PowerManagerClientStubImpl> update_timer_;
942  power_manager::PowerSupplyProperties props_;
943
944  // Number of callbacks returned by GetSuspendReadinessCallback() but not yet
945  // invoked.
946  int num_pending_suspend_readiness_callbacks_;
947
948  // Note: This should remain the last member so it'll be destroyed and
949  // invalidate its weak pointers before any other members are destroyed.
950  base::WeakPtrFactory<PowerManagerClientStubImpl> weak_ptr_factory_;
951};
952
953PowerManagerClient::PowerManagerClient() {
954}
955
956PowerManagerClient::~PowerManagerClient() {
957}
958
959// static
960PowerManagerClient* PowerManagerClient::Create(
961    DBusClientImplementationType type) {
962  if (type == REAL_DBUS_CLIENT_IMPLEMENTATION)
963    return new PowerManagerClientImpl();
964  DCHECK_EQ(STUB_DBUS_CLIENT_IMPLEMENTATION, type);
965  return new PowerManagerClientStubImpl();
966}
967
968}  // namespace chromeos
969