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..68d7e3b457a556f0e1c831c04aa43c4852a4e0c5 |
--- /dev/null |
+++ b/content/browser/battery_status/battery_status_manager_mac.cc |
@@ -0,0 +1,248 @@ |
+// 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 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, |
Mark Mentovai
2014/07/24 01:32:22
This is already in an unnamed namespace, so the st
Mark Mentovai
2014/07/24 01:32:22
Why did you choose CFIndex as the type for the CFN
timvolodine
2014/07/24 16:03:19
Done.
timvolodine
2014/07/24 16:03:19
usually it is kCFNumberSInt64Type, but can also be
Mark Mentovai
2014/07/24 16:08:52
timvolodine wrote:
timvolodine
2014/07/24 16:33:42
yes, my point was that the actual values are small
Mark Mentovai
2014/07/24 17:03:46
timvolodine wrote:
timvolodine
2014/07/25 12:46:08
Ok, changed to SInt32.
Technically casting a SInt
Mark Mentovai
2014/07/25 12:57:39
timvolodine wrote:
timvolodine
2014/07/25 13:13:17
right, thanks for the clarification and sorry for
|
+ CFStringRef key, |
+ CFIndex default_value) { |
+ CFNumberRef number = |
+ base::mac::GetValueFromDictionary<CFNumberRef>(description, key); |
+ CFIndex value; |
+ |
+ if (number && CFNumberGetValue(number, kCFNumberCFIndexType, &value)) |
+ return value; |
+ |
+ return default_value; |
+} |
+ |
+static Boolean GetValueAsBoolean(CFDictionaryRef description, |
Mark Mentovai
2014/07/24 01:32:21
I think that this should return a bool and take a
timvolodine
2014/07/24 16:03:19
Done.
|
+ CFStringRef key, |
+ Boolean default_value) { |
+ CFBooleanRef boolean = |
+ base::mac::GetValueFromDictionary<CFBooleanRef>(description, key); |
+ |
+ return boolean ? CFBooleanGetValue(boolean) : default_value; |
+} |
+ |
+static void FetchBatteryStatus(CFDictionaryRef description, |
+ blink::WebBatteryStatus& status) { |
+ CFStringRef currentState = |
Mark Mentovai
2014/07/24 01:32:22
Follow the C++ style guide. This should be named c
timvolodine
2014/07/24 16:03:19
Done.
|
+ base::mac::GetValueFromDictionary<CFStringRef>(description, |
+ CFSTR(kIOPSPowerSourceStateKey)); |
+ |
+ bool on_battery_power = CFStringCompare(currentState, |
Mark Mentovai
2014/07/24 01:32:22
I don’t think this is going to be too happy if cur
timvolodine
2014/07/24 16:03:19
actually kIOPSPowerSourceStateKey is a required ke
|
+ CFSTR(kIOPSBatteryPowerValue), |
+ 0) |
+ == kCFCompareEqualTo; |
+ bool is_charging = |
+ GetValueAsBoolean(description, CFSTR(kIOPSIsChargingKey), true); |
Mark Mentovai
2014/07/24 01:32:22
Is true the best default here? I probably would ha
timvolodine
2014/07/24 16:03:19
kIOPSIsChargingKey is also required so hopefully n
|
+ bool is_charged = |
+ GetValueAsBoolean(description, CFSTR(kIOPSIsChargedKey), false); |
+ |
+ // Set charging to false if on battery power and not charging. Otherwise leave |
+ // the default value, which is true. |
+ if (on_battery_power && !is_charging) |
Mark Mentovai
2014/07/24 01:32:22
Why not avoid the conditional and set this explici
timvolodine
2014/07/24 16:03:19
Done.
Re other fields: normally speaking the idea
|
+ status.charging = false; |
+ |
+ CFIndex current_capacity = |
+ GetValueAsCFIndex(description, CFSTR(kIOPSCurrentCapacityKey), -1); |
+ CFIndex max_capacity = |
+ GetValueAsCFIndex(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) { |
+ 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) |
+ ? 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) { |
+ CFIndex discharging_time = |
+ GetValueAsCFIndex(description, CFSTR(kIOPSTimeToEmptyKey), -1); |
+ if (discharging_time != -1) { |
+ status.dischargingTime = |
+ base::TimeDelta::FromMinutes(discharging_time).InSeconds(); |
+ } |
+ } |
+ } |
+} |
+ |
+static void OnBatteryStatusChanged(BatteryCallback& callback) { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
+ blink::WebBatteryStatus status; |
+ base::ScopedCFTypeRef<CFTypeRef> blob(IOPSCopyPowerSourcesInfo()); |
+ base::ScopedCFTypeRef<CFArrayRef> powerSourcesList( |
Mark Mentovai
2014/07/24 01:32:22
Naming: power_sources_list.
timvolodine
2014/07/24 16:03:19
Done.
|
+ IOPSCopyPowerSourcesList(blob)); |
+ CFIndex count = CFArrayGetCount(powerSourcesList); |
+ |
+ for (CFIndex i = 0; i < count; ++i) { |
+ CFDictionaryRef description = IOPSGetPowerSourceDescription(blob, |
+ CFArrayGetValueAtIndex(powerSourcesList, i)); |
Mark Mentovai
2014/07/24 01:32:21
if (!description)
continue;
can go here, you
timvolodine
2014/07/24 16:03:19
Done.
|
+ |
+ bool battery_present = GetValueAsBoolean(description, |
+ CFSTR(kIOPSIsPresentKey), |
+ false); |
+ |
+ if (!description || !battery_present) |
+ continue; |
+ |
+ FetchBatteryStatus(description, status); |
+ break; |
Mark Mentovai
2014/07/24 01:32:22
In theory, there can be more than one battery. If
timvolodine
2014/07/24 16:03:19
that's right, I've added a comment. I am reluctant
Mark Mentovai
2014/07/24 16:08:52
timvolodine wrote:
timvolodine
2014/07/24 16:33:42
the current specification does not strictly impose
Mark Mentovai
2014/07/24 17:03:46
timvolodine wrote:
timvolodine
2014/07/25 12:46:08
Done.
|
+ } |
+ |
+ callback.Run(status); |
+} |
+ |
+class BatteryStatusObserver |
+ : public base::RefCountedThreadSafe<BatteryStatusObserver> { |
+ public: |
+ explicit BatteryStatusObserver(BatteryCallback& callback) |
+ : callback_(callback) {} |
+ |
+ void Start() { |
+ // Need thread with UI-type message loop for notifications to run. |
Mark Mentovai
2014/07/24 01:32:21
If you added two more words here, it’d be a comple
timvolodine
2014/07/24 16:03:19
Done.
|
+ 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_); } |
+ |
+ void StartOnUI() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ if (notifier_run_loop_) |
+ return; |
+ |
+ OnBatteryStatusChangedUI((void*)&callback_); |
Mark Mentovai
2014/07/24 01:32:22
Use cplusplus<style>(casts). See the style guide.
timvolodine
2014/07/24 16:03:19
Done.
|
+ |
+ notifier_run_loop_.reset( |
+ IOPSNotificationCreateRunLoopSource(OnBatteryStatusChangedUI, |
+ (void*)&callback_)); |
+ if (notifier_run_loop_) { |
Mark Mentovai
2014/07/24 01:32:22
Do you need this conditional? If IOPSNotificationC
timvolodine
2014/07/24 16:03:19
I think we should avoid crashing the browser, inst
|
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), notifier_run_loop_, |
+ kCFRunLoopDefaultMode); |
+ } |
+ } |
+ |
+ void StopOnUI() { |
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
+ |
+ if (!notifier_run_loop_) |
+ return; |
+ |
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), notifier_run_loop_, |
+ kCFRunLoopDefaultMode); |
+ notifier_run_loop_.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_; |
+ |
+ 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 |