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 |