// Copyright 2014 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 "content/browser/battery_status/battery_status_manager.h" #include "base/memory/ref_counted.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/dbus/power_manager/power_supply_properties.pb.h" #include "chromeos/dbus/power_manager_client.h" #include "content/public/browser/browser_thread.h" #include "third_party/WebKit/public/platform/WebBatteryStatus.h" namespace content { namespace { class PowerManagerObserver : public chromeos::PowerManagerClient::Observer, public base::RefCountedThreadSafe<PowerManagerObserver> { public: explicit PowerManagerObserver( const BatteryStatusService::BatteryUpdateCallback& callback) : callback_(callback), currently_listening_(false) {} // Starts listening for updates. It is safe to call this on any thread. void Start() { if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { StartOnUI(); } else { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&PowerManagerObserver::StartOnUI, this)); } } // Stops listening for updates. It is safe to call this on any thread. void Stop() { if (BrowserThread::CurrentlyOn(BrowserThread::UI)) { StopOnUI(); } else { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&PowerManagerObserver::StopOnUI, this)); } } private: friend class base::RefCountedThreadSafe<PowerManagerObserver>; virtual ~PowerManagerObserver() {} bool IsBatteryPresent( const power_manager::PowerSupplyProperties& proto) const { return proto.battery_state() != power_manager::PowerSupplyProperties_BatteryState_NOT_PRESENT; } bool IsUsbChargerConnected( const power_manager::PowerSupplyProperties& proto) const { return proto.external_power() == power_manager::PowerSupplyProperties_ExternalPower_USB; } bool IsBatteryCharging( const power_manager::PowerSupplyProperties& proto) const { return proto.battery_state() != power_manager::PowerSupplyProperties_BatteryState_DISCHARGING; } bool IsBatteryFull(const power_manager::PowerSupplyProperties& proto) const { return proto.battery_state() == power_manager::PowerSupplyProperties_BatteryState_FULL; } double GetBatteryLevel( const power_manager::PowerSupplyProperties& proto) const { const double kMaxBatteryLevelProto = 100.f; return proto.battery_percent() / kMaxBatteryLevelProto; } void StartOnUI() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (currently_listening_) return; chromeos::PowerManagerClient* power_client = chromeos::DBusThreadManager::Get()->GetPowerManagerClient(); power_client->AddObserver(this); power_client->RequestStatusUpdate(); currently_listening_ = true; } void StopOnUI() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (!currently_listening_) return; chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver( this); currently_listening_ = false; } // chromeos::PowerManagerClient::Observer: virtual void PowerChanged( const power_manager::PowerSupplyProperties& proto) override { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); blink::WebBatteryStatus status; // Use the default values if there is no battery in the system. if (IsBatteryPresent(proto)) { // The charging status is unreliable if a low power charger is connected // (i.e. usb). bool status_unreliable = IsUsbChargerConnected(proto); // Battery time is unreliable if it is still being computed. bool time_unreliable = status_unreliable || proto.is_calculating_battery_time(); // Set |charging| only if the status is reliable. Otherwise, keep the // default (which is |true|). if (!status_unreliable) status.charging = IsBatteryCharging(proto); // Set |chargingTime| to +infinity if the battery is discharging, or if // the time is unreliable. Keep the default value (which is 0) if the // battery is full. if (time_unreliable || !status.charging) status.chargingTime = std::numeric_limits<double>::infinity(); else if (!IsBatteryFull(proto)) status.chargingTime = proto.battery_time_to_full_sec(); // Keep the default value for |dischargingTime| (which is +infinity) if // the time is unreliable, or if the battery is charging. if (!time_unreliable && !status.charging) status.dischargingTime = proto.battery_time_to_empty_sec(); status.level = GetBatteryLevel(proto); } callback_.Run(status); } BatteryStatusService::BatteryUpdateCallback callback_; bool currently_listening_; DISALLOW_COPY_AND_ASSIGN(PowerManagerObserver); }; class BatteryStatusManagerChromeOS : public BatteryStatusManager, public chromeos::PowerManagerClient::Observer { public: explicit BatteryStatusManagerChromeOS( const BatteryStatusService::BatteryUpdateCallback& callback) : observer_(new PowerManagerObserver(callback)) {} virtual ~BatteryStatusManagerChromeOS() { observer_->Stop(); } private: // BatteryStatusManager: virtual bool StartListeningBatteryChange() override { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); observer_->Start(); return true; } virtual void StopListeningBatteryChange() override { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); observer_->Stop(); } scoped_refptr<PowerManagerObserver> observer_; DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerChromeOS); }; } // namespace // static scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create( const BatteryStatusService::BatteryUpdateCallback& callback) { return scoped_ptr<BatteryStatusManager>( new BatteryStatusManagerChromeOS(callback)); } } // namespace content