Bumping gaia.json for 2 gaia revision(s) a=gaia-bump
[gecko.git] / hal / cocoa / CocoaBattery.cpp
blob749bbecad2faf0e58cd7d44b9522ebc340f5b914
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
38 public:
39 static MacPowerInformationService* GetInstance();
40 static void Shutdown();
41 static bool IsShuttingDown();
43 void BeginListening();
44 void StopListening();
46 static void HandleChange(void *aContext);
48 ~MacPowerInformationService();
50 private:
51 MacPowerInformationService();
53 // The reference to the runloop that is notified of power changes.
54 CFRunLoopSourceRef mRunLoopSource;
56 double mLevel;
57 bool mCharging;
58 double mRemainingTime;
59 bool mShouldNotify;
61 friend void GetCurrentBatteryInformation(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 MacPowerInformationService::sIOPSGetTimeRemainingEstimate;
74 * Implementation of mozilla::hal_impl::EnableBatteryNotifications,
75 * mozilla::hal_impl::DisableBatteryNotifications,
76 * and mozilla::hal_impl::GetCurrentBatteryInformation.
79 void
80 EnableBatteryNotifications()
82 if (!MacPowerInformationService::IsShuttingDown()) {
83 MacPowerInformationService::GetInstance()->BeginListening();
87 void
88 DisableBatteryNotifications()
90 if (!MacPowerInformationService::IsShuttingDown()) {
91 MacPowerInformationService::GetInstance()->StopListening();
95 void
96 GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo)
98 MacPowerInformationService* powerService = MacPowerInformationService::GetInstance();
100 aBatteryInfo->level() = powerService->mLevel;
101 aBatteryInfo->charging() = powerService->mCharging;
102 aBatteryInfo->remainingTime() = powerService->mRemainingTime;
105 bool MacPowerInformationService::sShuttingDown = false;
108 * Following is the implementation of MacPowerInformationService.
111 MacPowerInformationService* MacPowerInformationService::sInstance = nullptr;
113 namespace {
114 struct SingletonDestroyer MOZ_FINAL : public nsIObserver
116 NS_DECL_ISUPPORTS
117 NS_DECL_NSIOBSERVER
119 private:
120 ~SingletonDestroyer() {}
123 NS_IMPL_ISUPPORTS(SingletonDestroyer, nsIObserver)
125 NS_IMETHODIMP
126 SingletonDestroyer::Observe(nsISupports*, const char* aTopic, const char16_t*)
128 MOZ_ASSERT(!strcmp(aTopic, "xpcom-shutdown"));
129 MacPowerInformationService::Shutdown();
130 return NS_OK;
132 } // anonymous namespace
134 /* static */ MacPowerInformationService*
135 MacPowerInformationService::GetInstance()
137 if (sInstance) {
138 return sInstance;
141 sInstance = new MacPowerInformationService();
143 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
144 if (obs) {
145 obs->AddObserver(new SingletonDestroyer(), "xpcom-shutdown", false);
148 return sInstance;
151 bool
152 MacPowerInformationService::IsShuttingDown()
154 return sShuttingDown;
157 void
158 MacPowerInformationService::Shutdown()
160 sShuttingDown = true;
161 delete sInstance;
162 sInstance = nullptr;
165 MacPowerInformationService::MacPowerInformationService()
166 : mRunLoopSource(nullptr)
167 , mLevel(kDefaultLevel)
168 , mCharging(kDefaultCharging)
169 , mRemainingTime(kDefaultRemainingTime)
170 , mShouldNotify(false)
172 // IOPSGetTimeRemainingEstimate (and the related constants) are only available
173 // on 10.7, so we test for their presence at runtime.
174 sIOKitFramework = dlopen(IOKIT_FRAMEWORK_PATH, RTLD_LAZY | RTLD_LOCAL);
175 if (sIOKitFramework) {
176 sIOPSGetTimeRemainingEstimate =
177 (IOPSGetTimeRemainingEstimateFunc)dlsym(sIOKitFramework, "IOPSGetTimeRemainingEstimate");
178 } else {
179 sIOPSGetTimeRemainingEstimate = nullptr;
183 MacPowerInformationService::~MacPowerInformationService()
185 MOZ_ASSERT(!mRunLoopSource,
186 "The observers have not been correctly removed! "
187 "(StopListening should have been called)");
189 if (sIOKitFramework) {
190 dlclose(sIOKitFramework);
194 void
195 MacPowerInformationService::BeginListening()
197 // Set ourselves up to be notified about changes.
198 MOZ_ASSERT(!mRunLoopSource, "IOPS Notification Loop Source already set up. "
199 "(StopListening should have been called)");
201 mRunLoopSource = ::IOPSNotificationCreateRunLoopSource(HandleChange, this);
202 if (mRunLoopSource) {
203 ::CFRunLoopAddSource(::CFRunLoopGetCurrent(), mRunLoopSource,
204 kCFRunLoopDefaultMode);
206 // Invoke our callback now so we have data if GetCurrentBatteryInformation is
207 // called before a change happens.
208 HandleChange(this);
209 mShouldNotify = true;
213 void
214 MacPowerInformationService::StopListening()
216 MOZ_ASSERT(mRunLoopSource, "IOPS Notification Loop Source not set up. "
217 "(StopListening without BeginListening)");
219 ::CFRunLoopRemoveSource(::CFRunLoopGetCurrent(), mRunLoopSource,
220 kCFRunLoopDefaultMode);
221 mRunLoopSource = nullptr;
224 void
225 MacPowerInformationService::HandleChange(void* aContext) {
226 MacPowerInformationService* power =
227 static_cast<MacPowerInformationService*>(aContext);
229 CFTypeRef data = ::IOPSCopyPowerSourcesInfo();
230 if (!data) {
231 ::CFRelease(data);
232 return;
235 // Get the list of power sources.
236 CFArrayRef list = ::IOPSCopyPowerSourcesList(data);
237 if (!list) {
238 ::CFRelease(list);
239 return;
242 // Default values. These will be used if there are 0 sources or we can't find
243 // better information.
244 double level = kDefaultLevel;
245 double charging = kDefaultCharging;
246 double remainingTime = kDefaultRemainingTime;
248 // Look for the first battery power source to give us the information we need.
249 // Usually there's only 1 available, depending on current power source.
250 for (CFIndex i = 0; i < ::CFArrayGetCount(list); ++i) {
251 CFTypeRef source = ::CFArrayGetValueAtIndex(list, i);
252 CFDictionaryRef currPowerSourceDesc = ::IOPSGetPowerSourceDescription(data, source);
253 if (!currPowerSourceDesc) {
254 continue;
257 // Get a battery level estimate. This key is required.
258 int currentCapacity = 0;
259 const void* cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSCurrentCapacityKey));
260 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &currentCapacity);
262 // This key is also required.
263 int maxCapacity = 0;
264 cfRef = ::CFDictionaryGetValue(currPowerSourceDesc, CFSTR(kIOPSMaxCapacityKey));
265 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberSInt32Type, &maxCapacity);
267 if (maxCapacity > 0) {
268 level = static_cast<double>(currentCapacity)/static_cast<double>(maxCapacity);
271 // Find out if we're charging.
272 // This key is optional, we fallback to kDefaultCharging if the current power
273 // source doesn't have that info.
274 if(::CFDictionaryGetValueIfPresent(currPowerSourceDesc, CFSTR(kIOPSIsChargingKey), &cfRef)) {
275 charging = ::CFBooleanGetValue((CFBooleanRef)cfRef);
277 // Get an estimate of how long it's going to take until we're fully charged.
278 // This key is optional.
279 if (charging) {
280 // Default value that will be changed if we happen to find the actual
281 // remaining time.
282 remainingTime = level == 1.0 ? kDefaultRemainingTime : kUnknownRemainingTime;
284 if (::CFDictionaryGetValueIfPresent(currPowerSourceDesc,
285 CFSTR(kIOPSTimeToFullChargeKey), &cfRef)) {
286 int timeToCharge;
287 ::CFNumberGetValue((CFNumberRef)cfRef, kCFNumberIntType, &timeToCharge);
288 if (timeToCharge != kIOPSTimeRemainingUnknown) {
289 remainingTime = timeToCharge*60;
292 } else if (sIOPSGetTimeRemainingEstimate) { // not charging
293 // See if we can get a time estimate.
294 CFTimeInterval estimate = sIOPSGetTimeRemainingEstimate();
295 if (estimate == kIOPSTimeRemainingUnlimited || estimate == kIOPSTimeRemainingUnknown) {
296 remainingTime = kUnknownRemainingTime;
297 } else {
298 remainingTime = estimate;
303 break;
306 bool isNewData = level != power->mLevel || charging != power->mCharging ||
307 remainingTime != power->mRemainingTime;
309 power->mRemainingTime = remainingTime;
310 power->mCharging = charging;
311 power->mLevel = level;
313 // Notify the observers if stuff changed.
314 if (power->mShouldNotify && isNewData) {
315 hal::NotifyBatteryChange(hal::BatteryInformation(power->mLevel,
316 power->mCharging,
317 power->mRemainingTime));
320 ::CFRelease(data);
321 ::CFRelease(list);
324 } // namespace hal_impl
325 } // namespace mozilla