Bug 696045 - Implement Mac backend for Battery API. r=BenWa, mounir
☠☠ backed out by d28e07f4bec6 ☠ ☠
authorReuben Morais <reuben.morais@gmail.com>
Tue, 11 Sep 2012 20:07:06 -0400
changeset 113156 52120672ee11dd5bc24637f7e65cb072589e6d80
parent 113155 ef498cfb4fcd0ecacae21ada60dbbc7711a1c312
child 113157 7b86416037e112f3888b64727519fcaac990811b
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersBenWa, mounir
bugs696045
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 696045 - Implement Mac backend for Battery API. r=BenWa, mounir
hal/Makefile.in
hal/cocoa/CocoaBattery.cpp
--- a/hal/Makefile.in
+++ b/hal/Makefile.in
@@ -77,17 +77,17 @@ CPPSRCS += \
   WindowsSensor.cpp \
   FallbackVibration.cpp \
   FallbackScreenConfiguration.cpp \
   FallbackPower.cpp \
   FallbackAlarm.cpp \
   $(NULL)
 else ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += \
-  FallbackBattery.cpp \
+  CocoaBattery.cpp \
   FallbackVibration.cpp \
   FallbackPower.cpp \
   FallbackScreenConfiguration.cpp \
   FallbackAlarm.cpp \
   $(NULL)
 CMMSRCS += \
   CocoaSensor.mm \
   smslib.mm \
new file mode 100644
--- /dev/null
+++ b/hal/cocoa/CocoaBattery.cpp
@@ -0,0 +1,257 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#import <CoreFoundation/CoreFoundation.h>
+#import <IOKit/ps/IOPowerSources.h>
+#import <IOKit/ps/IOPSKeys.h>
+
+#include <mozilla/Hal.h>
+#include <mozilla/dom/battery/Constants.h>
+#include <mozilla/Services.h>
+
+#include <nsObserverService.h>
+
+using namespace mozilla::dom::battery;
+
+namespace mozilla {
+namespace hal_impl {
+
+class MacPowerInformationService
+{
+public:
+  static MacPowerInformationService* GetInstance();
+  static void Shutdown();
+
+  void BeginListening();
+  void StopListening();
+
+  static void HandleChange(void *aContext);
+
+  ~MacPowerInformationService();
+
+private:
+  MacPowerInformationService();
+
+  // The reference to the runloop that is notified of power changes.
+  CFRunLoopSourceRef mRunLoopSource;
+
+  double mLevel;
+  bool mCharging;
+  double mRemainingTime;
+  bool mShouldNotify;
+
+  friend void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo);
+
+  static MacPowerInformationService* sInstance;
+};
+
+/*
+ * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
+ *                   mozilla::hal_impl::DisableBatteryNotifications,
+ *               and mozilla::hal_impl::GetCurrentBatteryInformation.
+ */
+
+void
+EnableBatteryNotifications()
+{
+  MacPowerInformationService::GetInstance()->BeginListening();
+}
+
+void
+DisableBatteryNotifications()
+{
+  MacPowerInformationService::GetInstance()->StopListening();
+}
+
+void
+GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
+{
+  MacPowerInformationService* powerService = MacPowerInformationService::GetInstance();
+
+  aBatteryInfo->level() = powerService->mLevel;
+  aBatteryInfo->charging() = powerService->mCharging;
+  aBatteryInfo->remainingTime() = powerService->mRemainingTime;
+}
+
+/*
+ * Following is the implementation of MacPowerInformationService.
+ */
+
+MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
+
+namespace {
+struct SingletonDestroyer MOZ_FINAL : public nsIObserver
+{
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIOBSERVER
+};
+
+NS_IMPL_ISUPPORTS1(SingletonDestroyer, nsIObserver)
+
+NS_IMETHODIMP
+SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const PRUnichar*)
+{
+  MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+  MacPowerInformationService::Shutdown();
+  return NS_OK;
+}
+} // anonymous namespace
+
+/* static */ MacPowerInformationService*
+MacPowerInformationService::GetInstance()
+{
+  if (sInstance) {
+    return sInstance;
+  }
+  
+  sInstance = new MacPowerInformationService();
+
+  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+  if (obs) {
+    obs->AddObserver(new SingletonDestroyer(), "xpcom-shutdown", false);
+  }
+
+  return sInstance;
+}
+
+void
+MacPowerInformationService::Shutdown()
+{
+  delete sInstance;
+  sInstance = nullptr;
+}
+
+MacPowerInformationService::MacPowerInformationService()
+  : mRunLoopSource(nullptr)
+  , mLevel(kDefaultLevel)
+  , mCharging(kDefaultCharging)
+  , mRemainingTime(kDefaultRemainingTime)
+  , mShouldNotify(false)
+{
+}
+
+MacPowerInformationService::~MacPowerInformationService()
+{
+  MOZ_ASSERT(!mRunLoopSource,
+               "The observers have not been correctly removed! "
+               "(StopListening should have been called)");
+}
+
+void
+MacPowerInformationService::BeginListening()
+{
+  // Set ourselves up to be notified about changes.
+  MOZ_ASSERT(!mRunLoopSource, "IOPS Notification Loop Source already set up. "
+                              "(StopListening should have been called)");
+
+  mRunLoopSource = ::IOPSNotificationCreateRunLoopSource(HandleChange, this);
+  if (mRunLoopSource) {
+    ::CFRunLoopAddSource(::CFRunLoopGetCurrent(), mRunLoopSource,
+                         kCFRunLoopDefaultMode);
+
+    // Invoke our callback now so we have data if GetCurrentBatteryInformation is
+    // called before a change happens.
+    HandleChange(this);
+    mShouldNotify = true;
+  }
+}
+
+void
+MacPowerInformationService::StopListening()
+{
+  MOZ_ASSERT(mRunLoopSource, "IOPS Notification Loop Source not set up. "
+                             "(StopListening without BeginListening)");
+
+  ::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), mRunLoopSource,
+                          kCFRunLoopDefaultMode);
+  mRunLoopSource = nullptr;
+}
+
+void
+MacPowerInformationService::HandleChange(void* aContext) {
+  MacPowerInformationService* power =
+    static_cast<MacPowerInformationService*>(aContext);
+
+  CFTypeRef data = ::IOPSCopyPowerSourcesInfo();
+  if (!data) {
+    ::CFRelease(data);
+    return;
+  }
+
+  // Get the list of power sources.
+  CFArrayRef list = ::IOPSCopyPowerSourcesList(data);
+  if (!list) {
+    ::CFRelease(list);
+    return;
+  }
+
+  // Default values. These will be used if there are 0 sources or we can't find
+  // better information.
+  double level = kDefaultLevel;
+  double charging = kDefaultCharging;
+  double remainingTime = kDefaultRemainingTime;
+
+  // Look for the first battery power source to give us the information we need.
+  // Usually there's only 1 available, depending on current power source.
+  for (CFIndex i = 0; i < ::CFArrayGetCount(list); ++i) {
+    CFTypeRef source = ::CFArrayGetValueAtIndex(list, i);
+    CFDictionaryRef currPowerSourceDesc = ::IOPSGetPowerSourceDescription(data, source);
+    if (!currPowerSourceDesc) {
+      continue;
+    }
+
+    // See if we can get a time estimate.
+    CFTimeInterval estimate = ::IOPSGetTimeRemainingEstimate();
+    if (estimate == kIOPSTimeRemainingUnlimited) {
+      remainingTime = kUnknownRemainingTime;
+    } else if (estimate != kIOPSTimeRemainingUnknown) {
+      remainingTime = estimate;
+    }
+
+    // Get a battery level estimate. This key is required.
+    int currentCapacity = 0;
+    const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSCurrentCapacityKey));
+    ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &currentCapacity);
+
+    // This key is also required.
+    int maxCapacity = 0;
+    cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
+    ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
+
+    if (maxCapacity > 0) {
+      level = static_cast<double>(currentCapacity)/static_cast<double>(maxCapacity);
+    }
+
+    // Find out if we're charging.
+    // This key is optional, we fallback to kDefaultCharging if the current power
+    // source doesn't have that info.
+    if(::CFDictionaryGetValueIfPresent(currPowerSourceDesc, CFSTR(kIOPSIsChargingKey), &cfRef)) {
+      charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
+    }
+
+    break;
+  }
+
+  bool isNewData = level != power->mLevel || charging != power->mCharging ||
+                   remainingTime != power->mRemainingTime;
+
+  power->mRemainingTime = remainingTime;
+  power->mCharging = charging;
+  power->mLevel = level;
+
+  // Notify the observers if stuff changed.
+  if (power->mShouldNotify && isNewData) {
+    hal::NotifyBatteryChange(hal::BatteryInformation(power->mLevel,
+                                                     power->mCharging,
+                                                     power->mRemainingTime));
+  }
+
+  ::CFRelease(data);
+  ::CFRelease(list);
+}
+
+} // namespace hal_impl
+} // namespace mozilla