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