1// Copyright 2014 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 "content/browser/battery_status/battery_status_manager.h"
6
7#include "base/memory/ref_counted.h"
8#include "chromeos/dbus/dbus_thread_manager.h"
9#include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
10#include "chromeos/dbus/power_manager_client.h"
11#include "content/public/browser/browser_thread.h"
12#include "third_party/WebKit/public/platform/WebBatteryStatus.h"
13
14namespace content {
15
16namespace {
17
18class PowerManagerObserver
19    : public chromeos::PowerManagerClient::Observer,
20      public base::RefCountedThreadSafe<PowerManagerObserver> {
21 public:
22  explicit PowerManagerObserver(
23      const BatteryStatusService::BatteryUpdateCallback& callback)
24      : callback_(callback), currently_listening_(false) {}
25
26  // Starts listening for updates. It is safe to call this on any thread.
27  void Start() {
28    if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
29      StartOnUI();
30    } else {
31      BrowserThread::PostTask(
32          BrowserThread::UI,
33          FROM_HERE,
34          base::Bind(&PowerManagerObserver::StartOnUI, this));
35    }
36  }
37
38  // Stops listening for updates. It is safe to call this on any thread.
39  void Stop() {
40    if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
41      StopOnUI();
42    } else {
43      BrowserThread::PostTask(
44          BrowserThread::UI,
45          FROM_HERE,
46          base::Bind(&PowerManagerObserver::StopOnUI, this));
47    }
48  }
49
50 private:
51  friend class base::RefCountedThreadSafe<PowerManagerObserver>;
52
53  virtual ~PowerManagerObserver() {}
54
55  bool IsBatteryPresent(
56      const power_manager::PowerSupplyProperties& proto) const {
57    return proto.battery_state() !=
58           power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT;
59  }
60
61  bool IsUsbChargerConnected(
62      const power_manager::PowerSupplyProperties& proto) const {
63    return proto.external_power() ==
64           power_manager::PowerSupplyProperties_ExternalPower_USB;
65  }
66
67  bool IsBatteryCharging(
68      const power_manager::PowerSupplyProperties& proto) const {
69    return proto.battery_state() !=
70           power_manager::PowerSupplyProperties_BatteryState_DISCHARGING;
71  }
72
73  bool IsBatteryFull(const power_manager::PowerSupplyProperties& proto) const {
74    return proto.battery_state() ==
75           power_manager::PowerSupplyProperties_BatteryState_FULL;
76  }
77
78  double GetBatteryLevel(
79      const power_manager::PowerSupplyProperties& proto) const {
80    const double kMaxBatteryLevelProto = 100.f;
81    return proto.battery_percent() / kMaxBatteryLevelProto;
82  }
83
84  void StartOnUI() {
85    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
86    if (currently_listening_)
87      return;
88    chromeos::PowerManagerClient* power_client =
89        chromeos::DBusThreadManager::Get()->GetPowerManagerClient();
90    power_client->AddObserver(this);
91    power_client->RequestStatusUpdate();
92    currently_listening_ = true;
93  }
94
95  void StopOnUI() {
96    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
97    if (!currently_listening_)
98      return;
99    chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(
100        this);
101    currently_listening_ = false;
102  }
103
104  // chromeos::PowerManagerClient::Observer:
105  virtual void PowerChanged(
106      const power_manager::PowerSupplyProperties& proto) OVERRIDE {
107    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
108    blink::WebBatteryStatus status;
109    // Use the default values if there is no battery in the system.
110    if (IsBatteryPresent(proto)) {
111      // The charging status is unreliable if a low power charger is connected
112      // (i.e. usb).
113      bool status_unreliable = IsUsbChargerConnected(proto);
114      // Battery time is unreliable if it is still being computed.
115      bool time_unreliable =
116          status_unreliable || proto.is_calculating_battery_time();
117
118      // Set |charging| only if the status is reliable. Otherwise, keep the
119      // default (which is |true|).
120      if (!status_unreliable)
121        status.charging = IsBatteryCharging(proto);
122
123      // Set |chargingTime| to +infinity if the battery is discharging, or if
124      // the time is unreliable. Keep the default value (which is 0) if the
125      // battery is full.
126      if (time_unreliable || !status.charging)
127        status.chargingTime = std::numeric_limits<double>::infinity();
128      else if (!IsBatteryFull(proto))
129        status.chargingTime = proto.battery_time_to_full_sec();
130
131      // Keep the default value for |dischargingTime| (which is +infinity) if
132      // the time is unreliable, or if the battery is charging.
133      if (!time_unreliable && !status.charging)
134        status.dischargingTime = proto.battery_time_to_empty_sec();
135
136      status.level = GetBatteryLevel(proto);
137    }
138    callback_.Run(status);
139  }
140
141  BatteryStatusService::BatteryUpdateCallback callback_;
142  bool currently_listening_;
143
144  DISALLOW_COPY_AND_ASSIGN(PowerManagerObserver);
145};
146
147class BatteryStatusManagerChromeOS
148    : public BatteryStatusManager,
149      public chromeos::PowerManagerClient::Observer {
150 public:
151  explicit BatteryStatusManagerChromeOS(
152      const BatteryStatusService::BatteryUpdateCallback& callback)
153      : observer_(new PowerManagerObserver(callback)) {}
154
155  virtual ~BatteryStatusManagerChromeOS() { observer_->Stop(); }
156
157 private:
158  // BatteryStatusManager:
159  virtual bool StartListeningBatteryChange() OVERRIDE {
160    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
161    observer_->Start();
162    return true;
163  }
164
165  virtual void StopListeningBatteryChange() OVERRIDE {
166    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
167    observer_->Stop();
168  }
169
170  scoped_refptr<PowerManagerObserver> observer_;
171
172  DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerChromeOS);
173};
174
175}  // namespace
176
177// static
178scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
179    const BatteryStatusService::BatteryUpdateCallback& callback) {
180  return scoped_ptr<BatteryStatusManager>(
181      new BatteryStatusManagerChromeOS(callback));
182}
183
184}  // namespace content
185