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 "services/device/battery/battery_status_manager.h"
6 
7 #include <memory>
8 
9 #include "base/macros.h"
10 #include "base/memory/ref_counted.h"
11 #include "chromeos/dbus/power/power_manager_client.h"
12 #include "chromeos/dbus/power_manager/power_supply_properties.pb.h"
13 
14 namespace device {
15 
16 namespace {
17 
18 class PowerManagerObserver
19     : public chromeos::PowerManagerClient::Observer,
20       public base::RefCountedThreadSafe<PowerManagerObserver> {
21  public:
PowerManagerObserver(const BatteryStatusService::BatteryUpdateCallback & callback)22   explicit PowerManagerObserver(
23       const BatteryStatusService::BatteryUpdateCallback& callback)
24       : callback_(callback), currently_listening_(false) {}
25 
26   // Starts listening for updates.
Start()27   void Start() {
28     if (currently_listening_)
29       return;
30     chromeos::PowerManagerClient* power_client =
31         chromeos::PowerManagerClient::Get();
32     power_client->AddObserver(this);
33     power_client->RequestStatusUpdate();
34     currently_listening_ = true;
35   }
36 
37   // Stops listening for updates.
Stop()38   void Stop() {
39     if (!currently_listening_)
40       return;
41     chromeos::PowerManagerClient::Get()->RemoveObserver(this);
42     currently_listening_ = false;
43   }
44 
45  private:
46   friend class base::RefCountedThreadSafe<PowerManagerObserver>;
47 
~PowerManagerObserver()48   ~PowerManagerObserver() override {}
49 
IsBatteryPresent(const power_manager::PowerSupplyProperties & proto) const50   bool IsBatteryPresent(
51       const power_manager::PowerSupplyProperties& proto) const {
52     return proto.battery_state() !=
53            power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT;
54   }
55 
IsUsbChargerConnected(const power_manager::PowerSupplyProperties & proto) const56   bool IsUsbChargerConnected(
57       const power_manager::PowerSupplyProperties& proto) const {
58     return proto.external_power() ==
59            power_manager::PowerSupplyProperties_ExternalPower_USB;
60   }
61 
IsBatteryCharging(const power_manager::PowerSupplyProperties & proto) const62   bool IsBatteryCharging(
63       const power_manager::PowerSupplyProperties& proto) const {
64     return proto.battery_state() !=
65            power_manager::PowerSupplyProperties_BatteryState_DISCHARGING;
66   }
67 
IsBatteryFull(const power_manager::PowerSupplyProperties & proto) const68   bool IsBatteryFull(const power_manager::PowerSupplyProperties& proto) const {
69     return proto.battery_state() ==
70            power_manager::PowerSupplyProperties_BatteryState_FULL;
71   }
72 
GetBatteryLevel(const power_manager::PowerSupplyProperties & proto) const73   double GetBatteryLevel(
74       const power_manager::PowerSupplyProperties& proto) const {
75     const double kMaxBatteryLevelProto = 100.f;
76     return proto.battery_percent() / kMaxBatteryLevelProto;
77   }
78 
79   // chromeos::PowerManagerClient::Observer:
PowerChanged(const power_manager::PowerSupplyProperties & proto)80   void PowerChanged(
81       const power_manager::PowerSupplyProperties& proto) override {
82     mojom::BatteryStatus status;
83 
84     // Use the default values if there is no battery in the system.
85     if (IsBatteryPresent(proto)) {
86       // The charging status is unreliable if a low power charger is connected
87       // (i.e. usb).
88       bool status_unreliable = IsUsbChargerConnected(proto);
89       // Battery time is unreliable if it is still being computed.
90       bool time_unreliable =
91           status_unreliable || proto.is_calculating_battery_time();
92 
93       // Set |charging| only if the status is reliable. Otherwise, keep the
94       // default (which is |true|).
95       if (!status_unreliable)
96         status.charging = IsBatteryCharging(proto);
97 
98       // Set |chargingTime| to +infinity if the battery is discharging, or if
99       // the time is unreliable. Keep the default value (which is 0) if the
100       // battery is full.
101       if (time_unreliable || !status.charging)
102         status.charging_time = std::numeric_limits<double>::infinity();
103       else if (!IsBatteryFull(proto))
104         status.charging_time = proto.battery_time_to_full_sec();
105 
106       // Keep the default value for |dischargingTime| (which is +infinity) if
107       // the time is unreliable, or if the battery is charging.
108       if (!time_unreliable && !status.charging)
109         status.discharging_time = proto.battery_time_to_empty_sec();
110 
111       status.level = GetBatteryLevel(proto);
112     }
113     callback_.Run(status);
114   }
115 
116   BatteryStatusService::BatteryUpdateCallback callback_;
117   bool currently_listening_;
118 
119   DISALLOW_COPY_AND_ASSIGN(PowerManagerObserver);
120 };
121 
122 class BatteryStatusManagerChromeOS
123     : public BatteryStatusManager,
124       public chromeos::PowerManagerClient::Observer {
125  public:
BatteryStatusManagerChromeOS(const BatteryStatusService::BatteryUpdateCallback & callback)126   explicit BatteryStatusManagerChromeOS(
127       const BatteryStatusService::BatteryUpdateCallback& callback)
128       : observer_(base::MakeRefCounted<PowerManagerObserver>(callback)) {}
129 
~BatteryStatusManagerChromeOS()130   ~BatteryStatusManagerChromeOS() override { observer_->Stop(); }
131 
132  private:
133   // BatteryStatusManager:
StartListeningBatteryChange()134   bool StartListeningBatteryChange() override {
135     observer_->Start();
136     return true;
137   }
138 
StopListeningBatteryChange()139   void StopListeningBatteryChange() override { observer_->Stop(); }
140 
141   scoped_refptr<PowerManagerObserver> observer_;
142 
143   DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerChromeOS);
144 };
145 
146 }  // namespace
147 
148 // static
Create(const BatteryStatusService::BatteryUpdateCallback & callback)149 std::unique_ptr<BatteryStatusManager> BatteryStatusManager::Create(
150     const BatteryStatusService::BatteryUpdateCallback& callback) {
151   return std::make_unique<BatteryStatusManagerChromeOS>(callback);
152 }
153 
154 }  // namespace device
155