author | Reuben Morais <reuben.morais@gmail.com> |
Fri, 21 Sep 2012 23:36:14 -0400 | |
changeset 107789 | 294643a303c1b9fa4351a793406daf1ec7b8f0cf |
parent 107788 | 7c8b8d36679ff71e4b01e49a65674dfdb3b58253 |
child 107790 | b22c10ecf39093cbb540e3722245b4bbd638d885 |
push id | 23509 |
push user | ryanvm@gmail.com |
push date | Sat, 22 Sep 2012 12:28:38 +0000 |
treeherder | mozilla-central@b461a7cd250e [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | BenWa, mounir |
bugs | 696045 |
milestone | 18.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
|
hal/Makefile.in | file | annotate | diff | comparison | revisions | |
hal/cocoa/CocoaBattery.cpp | file | annotate | diff | comparison | revisions |
--- a/hal/Makefile.in +++ b/hal/Makefile.in @@ -78,17 +78,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,291 @@ +/* -*- 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> + +#include <dlfcn.h> + +#define IOKIT_FRAMEWORK_PATH "/System/Library/Frameworks/IOKit.framework/IOKit" + +#ifndef kIOPSTimeRemainingUnknown + #define kIOPSTimeRemainingUnknown ((CFTimeInterval)-1.0) +#endif +#ifndef kIOPSTimeRemainingUnlimited + #define kIOPSTimeRemainingUnlimited ((CFTimeInterval)-2.0) +#endif + +using namespace mozilla::dom::battery; + +namespace mozilla { +namespace hal_impl { + +typedef CFTimeInterval (*IOPSGetTimeRemainingEstimateFunc)(void); + +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; + + static void* sIOKitFramework; + static IOPSGetTimeRemainingEstimateFunc sIOPSGetTimeRemainingEstimate; +}; + +void* MacPowerInformationService::sIOKitFramework; +IOPSGetTimeRemainingEstimateFunc MacPowerInformationService::sIOPSGetTimeRemainingEstimate; + +/* + * 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) +{ + // IOPSGetTimeRemainingEstimate (and the related constants) are only available + // on 10.7, so we test for their presence at runtime. + sIOKitFramework = dlopen(IOKIT_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL); + if (sIOKitFramework) { + sIOPSGetTimeRemainingEstimate = + (IOPSGetTimeRemainingEstimateFunc)dlsym(sIOKitFramework, "IOPSGetTimeRemainingEstimate"); + } else { + sIOPSGetTimeRemainingEstimate = nullptr; + } +} + +MacPowerInformationService::~MacPowerInformationService() +{ + MOZ_ASSERT(!mRunLoopSource, + "The observers have not been correctly removed! " + "(StopListening should have been called)"); + + if (sIOKitFramework) { + dlclose(sIOKitFramework); + } +} + +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; + } + + if (sIOPSGetTimeRemainingEstimate) { + // See if we can get a time estimate. + CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate(); + if (estimate == kIOPSTimeRemainingUnlimited || estimate == kIOPSTimeRemainingUnknown) { + remainingTime = kUnknownRemainingTime; + } else { + 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, ¤tCapacity); + + // 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