| Index: content/browser/battery_status/battery_status_manager_mac.cc
|
| diff --git a/content/browser/battery_status/battery_status_manager_mac.cc b/content/browser/battery_status/battery_status_manager_mac.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8d6f35b3166d608ab9a2f7c7dd21e3a072304a3b
|
| --- /dev/null
|
| +++ b/content/browser/battery_status/battery_status_manager_mac.cc
|
| @@ -0,0 +1,268 @@
|
| +// 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 <CoreFoundation/CoreFoundation.h>
|
| +#include <IOKit/ps/IOPowerSources.h>
|
| +#include <IOKit/ps/IOPSKeys.h>
|
| +
|
| +#include "base/mac/foundation_util.h"
|
| +#include "base/mac/scoped_cftyperef.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/time/time.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "third_party/WebKit/public/platform/WebBatteryStatus.h"
|
| +
|
| +namespace content {
|
| +
|
| +namespace {
|
| +
|
| +typedef BatteryStatusService::BatteryUpdateCallback BatteryCallback;
|
| +
|
| +// Returns the value corresponding to |key| in the dictionary |description|.
|
| +// Returns |default_value| if the dictionary does not contain |key|, the
|
| +// corresponding value is NULL or it could not be converted to SInt64.
|
| +SInt64 GetValueAsSInt64(CFDictionaryRef description,
|
| + CFStringRef key,
|
| + SInt64 default_value) {
|
| + CFNumberRef number =
|
| + base::mac::GetValueFromDictionary<CFNumberRef>(description, key);
|
| + SInt64 value;
|
| +
|
| + if (number && CFNumberGetValue(number, kCFNumberSInt64Type, &value))
|
| + return value;
|
| +
|
| + return default_value;
|
| +}
|
| +
|
| +bool GetValueAsBoolean(CFDictionaryRef description,
|
| + CFStringRef key,
|
| + bool default_value) {
|
| + CFBooleanRef boolean =
|
| + base::mac::GetValueFromDictionary<CFBooleanRef>(description, key);
|
| +
|
| + return boolean ? CFBooleanGetValue(boolean) : default_value;
|
| +}
|
| +
|
| +bool CFStringsAreEqual(CFStringRef string1, CFStringRef string2) {
|
| + if (!string1 || !string2)
|
| + return false;
|
| + return CFStringCompare(string1, string2, 0) == kCFCompareEqualTo;
|
| +}
|
| +
|
| +void FetchBatteryStatus(CFDictionaryRef description,
|
| + blink::WebBatteryStatus& status) {
|
| + CFStringRef current_state =
|
| + base::mac::GetValueFromDictionary<CFStringRef>(description,
|
| + CFSTR(kIOPSPowerSourceStateKey));
|
| +
|
| + bool on_battery_power =
|
| + CFStringsAreEqual(current_state, CFSTR(kIOPSBatteryPowerValue));
|
| + bool is_charging =
|
| + GetValueAsBoolean(description, CFSTR(kIOPSIsChargingKey), true);
|
| + bool is_charged =
|
| + GetValueAsBoolean(description, CFSTR(kIOPSIsChargedKey), false);
|
| +
|
| + status.charging = !on_battery_power || is_charging;
|
| +
|
| + SInt64 current_capacity =
|
| + GetValueAsSInt64(description, CFSTR(kIOPSCurrentCapacityKey), -1);
|
| + SInt64 max_capacity =
|
| + GetValueAsSInt64(description, CFSTR(kIOPSMaxCapacityKey), -1);
|
| +
|
| + // Set level if it is available and valid. Otherwise leave the default value,
|
| + // which is 1.
|
| + if (current_capacity != -1 && max_capacity != -1 &&
|
| + current_capacity <= max_capacity && max_capacity != 0) {
|
| + status.level = current_capacity / static_cast<double>(max_capacity);
|
| + }
|
| +
|
| + if (is_charging) {
|
| + SInt64 charging_time =
|
| + GetValueAsSInt64(description, CFSTR(kIOPSTimeToFullChargeKey), -1);
|
| +
|
| + // Battery is charging: set the charging time if it's available, otherwise
|
| + // set to +infinity.
|
| + status.chargingTime = charging_time != -1
|
| + ? base::TimeDelta::FromMinutes(charging_time).InSeconds()
|
| + : std::numeric_limits<double>::infinity();
|
| + } else {
|
| + // Battery is not charging.
|
| + // Set chargingTime to +infinity if the battery is not charged. Otherwise
|
| + // leave the default value, which is 0.
|
| + if (!is_charged)
|
| + status.chargingTime = std::numeric_limits<double>::infinity();
|
| +
|
| + // Set dischargingTime if it's available and valid, i.e. when on battery
|
| + // power. Otherwise leave the default value, which is +infinity.
|
| + if (on_battery_power) {
|
| + SInt64 discharging_time =
|
| + GetValueAsSInt64(description, CFSTR(kIOPSTimeToEmptyKey), -1);
|
| + if (discharging_time != -1) {
|
| + status.dischargingTime =
|
| + base::TimeDelta::FromMinutes(discharging_time).InSeconds();
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +void OnBatteryStatusChanged(const BatteryCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + blink::WebBatteryStatus status;
|
| + base::ScopedCFTypeRef<CFTypeRef> info(IOPSCopyPowerSourcesInfo());
|
| + base::ScopedCFTypeRef<CFArrayRef> power_sources_list(
|
| + IOPSCopyPowerSourcesList(info));
|
| + CFIndex count = CFArrayGetCount(power_sources_list);
|
| +
|
| + bool internal_source_found = false;
|
| +
|
| + for (CFIndex i = 0; i < count; ++i) {
|
| + CFDictionaryRef description = IOPSGetPowerSourceDescription(info,
|
| + CFArrayGetValueAtIndex(power_sources_list, i));
|
| +
|
| + if (!description)
|
| + continue;
|
| +
|
| + CFStringRef transport_type =
|
| + base::mac::GetValueFromDictionary<CFStringRef>(description,
|
| + CFSTR(kIOPSTransportTypeKey));
|
| +
|
| + bool internal_source =
|
| + CFStringsAreEqual(transport_type, CFSTR(kIOPSInternalType));
|
| + bool source_present =
|
| + GetValueAsBoolean(description, CFSTR(kIOPSIsPresentKey), false);
|
| +
|
| + if (internal_source && source_present) {
|
| + // TODO(timvolodine): implement the case when there are multiple internal
|
| + // sources, e.g. when multiple batteries are present. Currently this will
|
| + // fail a DCHECK.
|
| + DCHECK(!internal_source_found);
|
| + FetchBatteryStatus(description, status);
|
| + internal_source_found = true;
|
| + }
|
| + }
|
| +
|
| + callback.Run(status);
|
| +}
|
| +
|
| +class BatteryStatusObserver
|
| + : public base::RefCountedThreadSafe<BatteryStatusObserver> {
|
| + public:
|
| + explicit BatteryStatusObserver(const BatteryCallback& callback)
|
| + : callback_(callback) {}
|
| +
|
| + void Start() {
|
| + // Need to start on a thread with UI-type message loop for
|
| + // |notifier_run_loop_| to receive callbacks.
|
| + if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
| + StartOnUI();
|
| + } else {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI,
|
| + FROM_HERE,
|
| + base::Bind(&BatteryStatusObserver::StartOnUI, this));
|
| + }
|
| + }
|
| +
|
| + void Stop() {
|
| + if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
|
| + StopOnUI();
|
| + } else {
|
| + BrowserThread::PostTask(
|
| + BrowserThread::UI,
|
| + FROM_HERE,
|
| + base::Bind(&BatteryStatusObserver::StopOnUI, this));
|
| + }
|
| + }
|
| +
|
| + private:
|
| + friend class base::RefCountedThreadSafe<BatteryStatusObserver>;
|
| + virtual ~BatteryStatusObserver() { DCHECK(!notifier_run_loop_source_); }
|
| +
|
| + void StartOnUI() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + if (notifier_run_loop_source_)
|
| + return;
|
| +
|
| + notifier_run_loop_source_.reset(
|
| + IOPSNotificationCreateRunLoopSource(OnBatteryStatusChangedUI,
|
| + static_cast<void*>(&callback_)));
|
| + if (!notifier_run_loop_source_) {
|
| + LOG(ERROR) << "Failed to create battery status notification run loop";
|
| + // Make sure to execute to callback with the default values.
|
| + callback_.Run(blink::WebBatteryStatus());
|
| + return;
|
| + }
|
| +
|
| + OnBatteryStatusChangedUI(static_cast<void*>(&callback_));
|
| + CFRunLoopAddSource(CFRunLoopGetCurrent(), notifier_run_loop_source_,
|
| + kCFRunLoopDefaultMode);
|
| + }
|
| +
|
| + void StopOnUI() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + if (!notifier_run_loop_source_)
|
| + return;
|
| +
|
| + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notifier_run_loop_source_,
|
| + kCFRunLoopDefaultMode);
|
| + notifier_run_loop_source_.reset();
|
| + }
|
| +
|
| + static void OnBatteryStatusChangedUI(void* callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + // Offload fetching of values and callback execution to the IO thread.
|
| + BrowserThread::PostTask(
|
| + BrowserThread::IO,
|
| + FROM_HERE,
|
| + base::Bind(&OnBatteryStatusChanged,
|
| + *static_cast<BatteryCallback*>(callback)));
|
| + }
|
| +
|
| + BatteryCallback callback_;
|
| + base::ScopedCFTypeRef<CFRunLoopSourceRef> notifier_run_loop_source_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver);
|
| +};
|
| +
|
| +class BatteryStatusManagerMac : public BatteryStatusManager {
|
| + public:
|
| + explicit BatteryStatusManagerMac(const BatteryCallback& callback)
|
| + : notifier_(new BatteryStatusObserver(callback)) {}
|
| +
|
| + virtual ~BatteryStatusManagerMac() {
|
| + notifier_->Stop();
|
| + }
|
| +
|
| + // BatteryStatusManager:
|
| + virtual bool StartListeningBatteryChange() OVERRIDE {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + notifier_->Start();
|
| + return true;
|
| + }
|
| +
|
| + virtual void StopListeningBatteryChange() OVERRIDE {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
|
| + notifier_->Stop();
|
| + }
|
| +
|
| + private:
|
| + scoped_refptr<BatteryStatusObserver> notifier_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(BatteryStatusManagerMac);
|
| +};
|
| +
|
| +} // end namespace
|
| +
|
| +// static
|
| +scoped_ptr<BatteryStatusManager> BatteryStatusManager::Create(
|
| + const BatteryStatusService::BatteryUpdateCallback& callback) {
|
| + return scoped_ptr<BatteryStatusManager>(
|
| + new BatteryStatusManagerMac(callback));
|
| +}
|
| +
|
| +} // namespace content
|
|
|