summaryrefslogtreecommitdiffstats
path: root/hal/cocoa/CocoaBattery.cpp
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /hal/cocoa/CocoaBattery.cpp
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'hal/cocoa/CocoaBattery.cpp')
-rw-r--r--hal/cocoa/CocoaBattery.cpp325
1 files changed, 325 insertions, 0 deletions
diff --git a/hal/cocoa/CocoaBattery.cpp b/hal/cocoa/CocoaBattery.cpp
new file mode 100644
index 000000000..6f1b7b1dc
--- /dev/null
+++ b/hal/cocoa/CocoaBattery.cpp
@@ -0,0 +1,325 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim set: 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 <nsIObserverService.h>
+#include <nsIObserver.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();
+ static bool IsShuttingDown();
+
+ 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 bool sShuttingDown;
+
+ 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()
+{
+ if (!MacPowerInformationService::IsShuttingDown()) {
+ MacPowerInformationService::GetInstance()->BeginListening();
+ }
+}
+
+void
+DisableBatteryNotifications()
+{
+ if (!MacPowerInformationService::IsShuttingDown()) {
+ MacPowerInformationService::GetInstance()->StopListening();
+ }
+}
+
+void
+GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
+{
+ MacPowerInformationService* powerService = MacPowerInformationService::GetInstance();
+
+ aBatteryInfo->level() = powerService->mLevel;
+ aBatteryInfo->charging() = powerService->mCharging;
+ aBatteryInfo->remainingTime() = powerService->mRemainingTime;
+}
+
+bool MacPowerInformationService::sShuttingDown = false;
+
+/*
+ * Following is the implementation of MacPowerInformationService.
+ */
+
+MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
+
+namespace {
+struct SingletonDestroyer final : public nsIObserver
+{
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+private:
+ ~SingletonDestroyer() {}
+};
+
+NS_IMPL_ISUPPORTS(SingletonDestroyer, nsIObserver)
+
+NS_IMETHODIMP
+SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const char16_t*)
+{
+ MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
+ MacPowerInformationService::Shutdown();
+ return NS_OK;
+}
+} // 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;
+}
+
+bool
+MacPowerInformationService::IsShuttingDown()
+{
+ return sShuttingDown;
+}
+
+void
+MacPowerInformationService::Shutdown()
+{
+ sShuttingDown = true;
+ 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;
+ }
+
+ // 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);
+
+ // Get an estimate of how long it's going to take until we're fully charged.
+ // This key is optional.
+ if (charging) {
+ // Default value that will be changed if we happen to find the actual
+ // remaining time.
+ remainingTime = level == 1.0 ? kDefaultRemainingTime : kUnknownRemainingTime;
+
+ if (::CFDictionaryGetValueIfPresent(currPowerSourceDesc,
+ CFSTR(kIOPSTimeToFullChargeKey), &cfRef)) {
+ int timeToCharge;
+ ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType, &timeToCharge);
+ if (timeToCharge != kIOPSTimeRemainingUnknown) {
+ remainingTime = timeToCharge*60;
+ }
+ }
+ } else if (sIOPSGetTimeRemainingEstimate) { // not charging
+ // See if we can get a time estimate.
+ CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
+ if (estimate == kIOPSTimeRemainingUnlimited || estimate == kIOPSTimeRemainingUnknown) {
+ remainingTime = kUnknownRemainingTime;
+ } else {
+ remainingTime = estimate;
+ }
+ }
+ }
+
+ 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