Chromium Code Reviews| 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..6bc5146e92a5c945be671c9cb190af4d60952a14 |
| --- /dev/null |
| +++ b/content/browser/battery_status/battery_status_manager_mac.cc |
| @@ -0,0 +1,234 @@ |
| +// 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/memory/ref_counted.h" |
| +#include "base/threading/thread.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 const 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 CFIndexType. |
| +static CFIndex GetValueAsCFIndex(CFDictionaryRef description, |
| + CFStringRef key, |
| + CFIndex default_value) { |
| + CFNumberRef value_number = |
| + (CFNumberRef)CFDictionaryGetValue(description, key); |
|
Mark Mentovai
2014/07/17 14:08:31
CFCast<CFNumberRef>.
Robert Sesek
2014/07/17 15:16:41
Better yet: base::mac::GetValueFromDictionary<T>(d
timvolodine
2014/07/22 18:04:43
Done.
timvolodine
2014/07/22 18:04:43
Done.
|
| + CFIndex value; |
| + |
| + if (value_number && |
| + CFNumberGetValue(value_number, kCFNumberCFIndexType, &value)) |
| + return value; |
| + |
| + return default_value; |
| +} |
| + |
| +static void FetchBatteryStatus(CFDictionaryRef description, |
| + blink::WebBatteryStatus& status) { |
| + CFStringRef currentState = |
| + (CFStringRef)CFDictionaryGetValue(description, |
|
Mark Mentovai
2014/07/17 14:08:31
CFCast<CFStringRef>.
timvolodine
2014/07/22 18:04:44
Done.
|
| + CFSTR(kIOPSPowerSourceStateKey)); |
| + bool on_battery_power = |
| + CFStringCompare(currentState, CFSTR(kIOPSBatteryPowerValue), 0) |
| + == kCFCompareEqualTo; |
| + bool is_charging = |
| + CFDictionaryGetValue(description, CFSTR(kIOPSIsChargingKey)) |
|
Mark Mentovai
2014/07/17 14:08:31
This just tells you if kIOPSIsChargingKey was foun
timvolodine
2014/07/22 18:04:44
I've changed this to use CFBooleanGetValue, as you
|
| + == kCFBooleanTrue; |
| + bool is_charged = |
| + CFDictionaryGetValue(description, CFSTR(kIOPSIsChargedKey)) |
| + == kCFBooleanTrue; |
| + |
| + // Set charging to false if on battery power and not charging. |
|
Mark Mentovai
2014/07/17 14:08:31
Why not set status.charging to is_charging?
Why h
timvolodine
2014/07/22 18:04:44
yes this is on purpose. I've added a comment expla
|
| + if (on_battery_power && !is_charging) |
| + status.charging = false; |
| + |
| + CFIndex current_capacity = |
| + GetValueAsCFIndex(description, CFSTR(kIOPSCurrentCapacityKey), -1); |
| + CFIndex max_capacity = |
| + GetValueAsCFIndex(description, CFSTR(kIOPSMaxCapacityKey), -1); |
| + |
| + if (current_capacity != -1 && max_capacity != -1) |
|
Mark Mentovai
2014/07/17 14:08:31
&& current_capacity <= max_capacity?
&& max_capaci
timvolodine
2014/07/22 18:04:44
Done.
|
| + status.level = (double)current_capacity/(double)max_capacity; |
|
Mark Mentovai
2014/07/17 14:08:31
Don’t use (C_style)casts, use Cplusplus<style>(cas
timvolodine
2014/07/22 18:04:43
Done.
|
| + |
| + if (is_charging) { |
| + CFIndex charging_time = |
| + GetValueAsCFIndex(description, CFSTR(kIOPSTimeToFullChargeKey), -1); |
| + |
| + // Battery is charging: set the charging time if it's available, otherwise |
| + // set to +infinity. |
| + status.chargingTime = (charging_time != -1) |
|
Mark Mentovai
2014/07/17 14:08:31
WebBatteryStatus.h doesn’t have comments in it to
timvolodine
2014/07/22 18:04:44
The specification is here: https://dvcs.w3.org/hg/
|
| + ? 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. |
| + if (!is_charged) |
| + status.chargingTime = std::numeric_limits<double>::infinity(); |
| + |
| + // Set dischargingTime if it's available and valid, i.e. when on battery |
| + // power. |
| + if (on_battery_power) { |
| + CFIndex discharging_time = |
| + GetValueAsCFIndex(description, CFSTR(kIOPSTimeToEmptyKey), -1); |
| + if (discharging_time != -1) { |
| + status.dischargingTime = |
|
Mark Mentovai
2014/07/17 14:08:31
Should you be setting status.dischargingTime to so
timvolodine
2014/07/22 18:04:44
no garbage ;). I've added some comments regarding
|
| + base::TimeDelta::FromMinutes(discharging_time).InSeconds(); |
| + } |
| + } |
| + } |
| +} |
| + |
| +static void OnBatteryStatusChanged(BatteryCallback& callback) { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + blink::WebBatteryStatus status; |
| + CFTypeRef blob = IOPSCopyPowerSourcesInfo(); |
|
Mark Mentovai
2014/07/17 14:08:31
Do you need to release this or does IOPSCopyPowerS
timvolodine
2014/07/22 18:04:44
you are right, it needs to be released. added Scop
|
| + CFArrayRef powerSourcesList = IOPSCopyPowerSourcesList(blob); |
|
Mark Mentovai
2014/07/17 14:08:31
This one almost definitely needs a ScopedCFTypeRef
timvolodine
2014/07/22 18:04:44
added ScopedCFTypeRef, done.
|
| + CFIndex count = CFArrayGetCount(powerSourcesList); |
| + |
| + for (CFIndex i = 0; i < count; ++i) { |
| + CFDictionaryRef description = IOPSGetPowerSourceDescription(blob, |
| + CFArrayGetValueAtIndex(powerSourcesList, i)); |
| + |
| + bool battery_present = |
| + CFDictionaryGetValue(description, CFSTR(kIOPSIsPresentKey)) |
|
Mark Mentovai
2014/07/17 14:08:31
CFDictionaryGetValue doesn’t return kCFBooleanTrue
timvolodine
2014/07/22 18:04:44
Done. (but it looks like it actually returns a poi
|
| + == kCFBooleanTrue; |
| + |
| + if (!description || !battery_present) |
| + continue; |
| + |
| + FetchBatteryStatus(description, status); |
| + break; |
| + } |
| + |
| + callback.Run(status); |
| +} |
| + |
| +class BatteryStatusObserver |
| + : public base::RefCountedThreadSafe<BatteryStatusObserver> { |
| + public: |
| + explicit BatteryStatusObserver(BatteryCallback& callback) |
| + : callback_(callback), notifier_run_loop_(0) {} |
|
Mark Mentovai
2014/07/17 14:08:31
Use NULL for null CFTypeRef-family values here and
timvolodine
2014/07/22 18:04:43
not necessary anymore because I made notifier_run_
|
| + |
| + void Start() { |
| + // Need thread with UI-type message loop for notifications to run. |
| + 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() {} |
|
Mark Mentovai
2014/07/17 14:08:31
[D]CHECK that it’s stopped (by notifier_run_loop_
timvolodine
2014/07/22 18:04:44
Done.
|
| + |
| + void StartOnUI() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + if (notifier_run_loop_) |
|
Mark Mentovai
2014/07/17 14:08:31
Might this actually happen in practice? It may be
timvolodine
2014/07/22 18:04:44
I don't think we should be that strict, users may
|
| + return; |
| + |
| + OnBatteryStatusChangedUI((void*)&callback_); |
| + |
| + notifier_run_loop_ = |
| + IOPSNotificationCreateRunLoopSource(OnBatteryStatusChangedUI, |
| + (void*)&callback_); |
| + if (notifier_run_loop_) { |
| + CFRunLoopAddSource(CFRunLoopGetCurrent(), notifier_run_loop_, |
| + kCFRunLoopDefaultMode); |
| + } |
| + } |
| + |
| + void StopOnUI() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| + |
| + if (!notifier_run_loop_) |
| + return; |
| + |
| + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notifier_run_loop_, |
| + kCFRunLoopDefaultMode); |
| + CFRelease(notifier_run_loop_); |
| + notifier_run_loop_ = 0; |
| + } |
| + |
| + 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_; |
|
Robert Sesek
2014/07/17 15:16:41
Why is this stored by reference?
timvolodine
2014/07/22 18:04:43
technically this is ok because the callback is own
|
| + CFRunLoopSourceRef notifier_run_loop_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(BatteryStatusObserver); |
| +}; |
| + |
| +class BatteryStatusManagerMac : public BatteryStatusManager { |
| + public: |
| + explicit BatteryStatusManagerMac(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 |