Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / hal / cocoa / CocoaBattery.cpp
bloba6c4b689e12b6b625f0d8a102fe9318a603f263b
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim set: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #import <CoreFoundation/CoreFoundation.h>
8 #import <IOKit/ps/IOPowerSources.h>
9 #import <IOKit/ps/IOPSKeys.h>
11 #include <mozilla/Hal.h>
12 #include <mozilla/dom/battery/Constants.h>
13 #include <mozilla/Services.h>
15 #include <nsIObserverService.h>
16 #include <nsIObserver.h>
18 #include <dlfcn.h>
20 #define IOKIT_FRAMEWORK_PATH "/System/Library/Frameworks/IOKit.framework/IOKit"
22 #ifndef kIOPSTimeRemainingUnknown
23 # define kIOPSTimeRemainingUnknown ((CFTimeInterval)-1.0)
24 #endif
25 #ifndef kIOPSTimeRemainingUnlimited
26 # define kIOPSTimeRemainingUnlimited ((CFTimeInterval)-2.0)
27 #endif
29 using namespace mozilla::dom::battery;
31 namespace mozilla {
32 namespace hal_impl {
34 typedef CFTimeInterval (*IOPSGetTimeRemainingEstimateFunc)(void);
36 class MacPowerInformationService {
37 public:
38 static MacPowerInformationService* GetInstance();
39 static void Shutdown();
40 static bool IsShuttingDown();
42 void BeginListening();
43 void StopListening();
45 static void HandleChange(void* aContext);
47 ~MacPowerInformationService();
49 private:
50 MacPowerInformationService();
52 // The reference to the runloop that is notified of power changes.
53 CFRunLoopSourceRef mRunLoopSource;
55 double mLevel;
56 bool mCharging;
57 double mRemainingTime;
58 bool mShouldNotify;
60 friend void GetCurrentBatteryInformation(
61 hal::BatteryInformation* aBatteryInfo);
63 static MacPowerInformationService* sInstance;
64 static bool sShuttingDown;
66 static void* sIOKitFramework;
67 static IOPSGetTimeRemainingEstimateFunc sIOPSGetTimeRemainingEstimate;
70 void* MacPowerInformationService::sIOKitFramework;
71 IOPSGetTimeRemainingEstimateFunc
72 MacPowerInformationService::sIOPSGetTimeRemainingEstimate;
75 * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
76 * mozilla::hal_impl::DisableBatteryNotifications,
77 * and mozilla::hal_impl::GetCurrentBatteryInformation.
80 void EnableBatteryNotifications() {
81 if (!MacPowerInformationService::IsShuttingDown()) {
82 MacPowerInformationService::GetInstance()->BeginListening();
86 void DisableBatteryNotifications() {
87 if (!MacPowerInformationService::IsShuttingDown()) {
88 MacPowerInformationService::GetInstance()->StopListening();
92 void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo) {
93 MacPowerInformationService* powerService =
94 MacPowerInformationService::GetInstance();
96 aBatteryInfo->level() = powerService->mLevel;
97 aBatteryInfo->charging() = powerService->mCharging;
98 aBatteryInfo->remainingTime() = powerService->mRemainingTime;
101 bool MacPowerInformationService::sShuttingDown = false;
104 * Following is the implementation of MacPowerInformationService.
107 MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
109 namespace {
110 struct SingletonDestroyer final : public nsIObserver {
111 NS_DECL_ISUPPORTS
112 NS_DECL_NSIOBSERVER
114 private:
115 ~SingletonDestroyer() {}
118 NS_IMPL_ISUPPORTS(SingletonDestroyer, nsIObserver)
120 NS_IMETHODIMP
121 SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const char16_t*) {
122 MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
123 MacPowerInformationService::Shutdown();
124 return NS_OK;
126 } // namespace
128 /* static */
129 MacPowerInformationService* MacPowerInformationService::GetInstance() {
130 if (sInstance) {
131 return sInstance;
134 sInstance = new MacPowerInformationService();
136 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
137 if (obs) {
138 obs->AddObserver(new SingletonDestroyer(), "xpcom-shutdown", false);
141 return sInstance;
144 bool MacPowerInformationService::IsShuttingDown() { return sShuttingDown; }
146 void MacPowerInformationService::Shutdown() {
147 sShuttingDown = true;
148 delete sInstance;
149 sInstance = nullptr;
152 MacPowerInformationService::MacPowerInformationService()
153 : mRunLoopSource(nullptr),
154 mLevel(kDefaultLevel),
155 mCharging(kDefaultCharging),
156 mRemainingTime(kDefaultRemainingTime),
157 mShouldNotify(false) {
158 // IOPSGetTimeRemainingEstimate (and the related constants) are only available
159 // on 10.7, so we test for their presence at runtime.
160 sIOKitFramework = dlopen(IOKIT_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
161 if (sIOKitFramework) {
162 sIOPSGetTimeRemainingEstimate = (IOPSGetTimeRemainingEstimateFunc)dlsym(
163 sIOKitFramework, "IOPSGetTimeRemainingEstimate");
164 } else {
165 sIOPSGetTimeRemainingEstimate = nullptr;
169 MacPowerInformationService::~MacPowerInformationService() {
170 MOZ_ASSERT(!mRunLoopSource,
171 "The observers have not been correctly removed! "
172 "(StopListening should have been called)");
174 if (sIOKitFramework) {
175 dlclose(sIOKitFramework);
179 void MacPowerInformationService::BeginListening() {
180 // Set ourselves up to be notified about changes.
181 MOZ_ASSERT(!mRunLoopSource,
182 "IOPS Notification Loop Source already set up. "
183 "(StopListening should have been called)");
185 mRunLoopSource = ::IOPSNotificationCreateRunLoopSource(HandleChange, this);
186 if (mRunLoopSource) {
187 ::CFRunLoopAddSource(::CFRunLoopGetCurrent(), mRunLoopSource,
188 kCFRunLoopDefaultMode);
190 // Invoke our callback now so we have data if GetCurrentBatteryInformation
191 // is called before a change happens.
192 HandleChange(this);
193 mShouldNotify = true;
197 void MacPowerInformationService::StopListening() {
198 if (mRunLoopSource) {
199 ::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), mRunLoopSource,
200 kCFRunLoopDefaultMode);
201 mRunLoopSource = nullptr;
205 void MacPowerInformationService::HandleChange(void* aContext) {
206 MacPowerInformationService* power =
207 static_cast<MacPowerInformationService*>(aContext);
209 CFTypeRef data = ::IOPSCopyPowerSourcesInfo();
210 if (!data) {
211 ::CFRelease(data);
212 return;
215 // Get the list of power sources.
216 CFArrayRef list = ::IOPSCopyPowerSourcesList(data);
217 if (!list) {
218 ::CFRelease(list);
219 return;
222 // Default values. These will be used if there are 0 sources or we can't find
223 // better information.
224 double level = kDefaultLevel;
225 double charging = kDefaultCharging;
226 double remainingTime = kDefaultRemainingTime;
228 // Look for the first battery power source to give us the information we need.
229 // Usually there's only 1 available, depending on current power source.
230 for (CFIndex i = 0; i < ::CFArrayGetCount(list); ++i) {
231 CFTypeRef source = ::CFArrayGetValueAtIndex(list, i);
232 CFDictionaryRef currPowerSourceDesc =
233 ::IOPSGetPowerSourceDescription(data, source);
234 if (!currPowerSourceDesc) {
235 continue;
238 // Get a battery level estimate. This key is required but does not always
239 // exist.
240 int currentCapacity = 0;
241 const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc,
242 CFSTR(kIOPSCurrentCapacityKey));
243 if (!cfRef) {
244 continue;
246 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type,
247 &currentCapacity);
249 // This key is also required.
250 int maxCapacity = 0;
251 cfRef =
252 ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
253 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
255 if (maxCapacity > 0) {
256 level = static_cast<double>(currentCapacity) /
257 static_cast<double>(maxCapacity);
260 // Find out if we're charging.
261 // This key is optional, we fallback to kDefaultCharging if the current
262 // power source doesn't have that info.
263 if (::CFDictionaryGetValueIfPresent(currPowerSourceDesc,
264 CFSTR(kIOPSIsChargingKey), &cfRef)) {
265 charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
267 // Get an estimate of how long it's going to take until we're fully
268 // charged. This key is optional.
269 if (charging) {
270 // Default value that will be changed if we happen to find the actual
271 // remaining time.
272 remainingTime =
273 level == 1.0 ? kDefaultRemainingTime : kUnknownRemainingTime;
275 if (::CFDictionaryGetValueIfPresent(
276 currPowerSourceDesc, CFSTR(kIOPSTimeToFullChargeKey), &cfRef)) {
277 int timeToCharge;
278 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType,
279 &timeToCharge);
280 if (timeToCharge != kIOPSTimeRemainingUnknown) {
281 remainingTime = timeToCharge * 60;
284 } else if (sIOPSGetTimeRemainingEstimate) { // not charging
285 // See if we can get a time estimate.
286 CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
287 if (estimate == kIOPSTimeRemainingUnlimited ||
288 estimate == kIOPSTimeRemainingUnknown) {
289 remainingTime = kUnknownRemainingTime;
290 } else {
291 remainingTime = estimate;
296 break;
299 bool isNewData = level != power->mLevel || charging != power->mCharging ||
300 remainingTime != power->mRemainingTime;
302 power->mRemainingTime = remainingTime;
303 power->mCharging = charging;
304 power->mLevel = level;
306 // Notify the observers if stuff changed.
307 if (power->mShouldNotify && isNewData) {
308 hal::NotifyBatteryChange(hal::BatteryInformation(
309 power->mLevel, power->mCharging, power->mRemainingTime));
312 ::CFRelease(data);
313 ::CFRelease(list);
316 } // namespace hal_impl
317 } // namespace mozilla