hal/cocoa/CocoaBattery.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

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

mercurial