1/* 2 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23/* 24 * HISTORY 25 * 26 */ 27 28 29#include <sys/cdefs.h> 30#include <notify.h> 31 32#include <CoreFoundation/CoreFoundation.h> 33#include "IOSystemConfiguration.h" 34#include "IOPSKeys.h" 35#include "IOPowerSources.h" 36#include "IOPowerSourcesPrivate.h" 37#include <IOKit/pwr_mgt/IOPMLibPrivate.h> 38#include <notify.h> 39 40#ifndef kIOPSDynamicStorePathKey 41#define kIOPSDynamicStorePathKey kIOPSDynamicStorePath 42#endif 43 44#ifndef kIOPSDynamicStoreLowBattPathKey 45#define kIOPSDynamicStoreLowBattPathKey "/IOKit/LowBatteryWarning" 46#endif 47 48#ifndef kIOPSDynamicStorePowerAdapterKey 49#define kIOPSDynamicStorePowerAdapterKey "/IOKit/PowerAdapter" 50#endif 51 52#if TARGET_OS_EMBEDDED 53#define kIOPSDynamicStoreFullPath "State:/IOKit/PowerSources/InternalBattery-0" 54#endif 55 56IOPSLowBatteryWarningLevel IOPSGetBatteryWarningLevel(void) 57{ 58 SCDynamicStoreRef store = NULL; 59 CFStringRef key = NULL; 60 CFNumberRef scWarnValue = NULL; 61 int return_level = kIOPSLowBatteryWarningNone; 62 63 store = SCDynamicStoreCreate(kCFAllocatorDefault, 64 CFSTR("IOKit Power Source Copy"), NULL, NULL); 65 if (!store) 66 goto SAD_EXIT; 67 68 key = SCDynamicStoreKeyCreate( 69 kCFAllocatorDefault, 70 CFSTR("%@%@"), 71 _io_kSCDynamicStoreDomainState, 72 CFSTR(kIOPSDynamicStoreLowBattPathKey)); 73 if (!key) 74 goto SAD_EXIT; 75 76 scWarnValue = isA_CFNumber(SCDynamicStoreCopyValue(store, key)); 77 if (scWarnValue) { 78 79 CFNumberGetValue(scWarnValue, kCFNumberIntType, &return_level); 80 81 CFRelease(scWarnValue); 82 scWarnValue = NULL; 83 } 84 85SAD_EXIT: 86 if (store) CFRelease(store); 87 if (key) CFRelease(key); 88 return return_level; 89} 90 91 92// powerd uses these same constants to define the bitfields in the 93// kIOPSTimeRemainingNotificationKey key 94#define _kPSTimeRemainingNotifyExternalBit (1 << 16) 95#define _kPSTimeRemainingNotifyChargingBit (1 << 17) 96#define _kPSTimeRemainingNotifyUnknownBit (1 << 18) 97#define _kPSTimeRemainingNotifyValidBit (1 << 19) 98#define _kSecondsPerMinute ((CFTimeInterval)60.0) 99CFTimeInterval IOPSGetTimeRemainingEstimate(void) 100{ 101 int myNotifyToken = 0; 102 uint64_t packedBatteryData = 0; 103 int myNotifyStatus = 0; 104 105 myNotifyStatus = notify_register_check(kIOPSTimeRemainingNotificationKey, &myNotifyToken); 106 107 if (NOTIFY_STATUS_OK != myNotifyStatus) { 108 // FAILURE: We return an optimistic unlimited time remaining estimate 109 // if we don't know the truth. 110 return kIOPSTimeRemainingUnlimited; 111 } 112 113 notify_get_state(myNotifyToken, &packedBatteryData); 114 115 notify_cancel(myNotifyToken); 116 117 if (!(packedBatteryData & _kPSTimeRemainingNotifyValidBit) 118 || (packedBatteryData & _kPSTimeRemainingNotifyExternalBit)) { 119 return kIOPSTimeRemainingUnlimited; 120 } 121 122 if (packedBatteryData & _kPSTimeRemainingNotifyUnknownBit) { 123 return kIOPSTimeRemainingUnknown; 124 } 125 126 return (_kSecondsPerMinute * (CFTimeInterval)(packedBatteryData & 0xFFFF)); 127} 128 129 130CFDictionaryRef IOPSCopyExternalPowerAdapterDetails(void) 131{ 132 SCDynamicStoreRef store = NULL; 133 CFStringRef key = NULL; 134 CFDictionaryRef ret_dict = NULL; 135 136 store = SCDynamicStoreCreate(kCFAllocatorDefault, 137 CFSTR("IOKit Power Source Copy"), NULL, NULL); 138 if (!store) 139 goto SAD_EXIT; 140 141 key = SCDynamicStoreKeyCreate( 142 kCFAllocatorDefault, 143 CFSTR("%@%@"), 144 _io_kSCDynamicStoreDomainState, 145 CFSTR(kIOPSDynamicStorePowerAdapterKey)); 146 if (!key) 147 goto SAD_EXIT; 148 149 ret_dict = isA_CFDictionary(SCDynamicStoreCopyValue(store, key)); 150 151SAD_EXIT: 152 if (store) CFRelease(store); 153 if (key) CFRelease(key); 154 return ret_dict; 155} 156 157static CFArrayRef CreatePSKeysArray(void) 158{ 159 CFStringRef ps_match = NULL; 160 CFMutableArrayRef ps_arr = NULL; 161 162#if TARGET_OS_EMBEDDED 163 // Doing a regex match on iOS is unnecessary as there is always only 1 164 // power source, whose identity is known. 165 // Optimization for <rdar://problem/11177160> 166 ps_match = SCDynamicStoreKeyCreate(kCFAllocatorDefault, 167 CFSTR(kIOPSDynamicStoreFullPath)); 168#else 169 // Create regular expression to match all Power Sources 170 ps_match = SCDynamicStoreKeyCreate( 171 kCFAllocatorDefault, 172 CFSTR("%@%@/%@"), 173 _io_kSCDynamicStoreDomainState, 174 CFSTR(kIOPSDynamicStorePath), 175 _io_kSCCompAnyRegex); 176#endif 177 178 if(!ps_match) return NULL; 179 ps_arr = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 180 if(!ps_arr) return NULL; 181 CFArrayAppendValue(ps_arr, ps_match); 182 CFRelease(ps_match); 183 184 return (CFArrayRef)ps_arr; 185} 186 187 188/*** 189 Returns a blob of Power Source information in an opaque CFTypeRef. Clients should 190 not actually look directly at data in the CFTypeRef - they should use the accessor 191 functions IOPSCopyPowerSourcesList and IOPSGetPowerSourceDescription, instead. 192 Returns NULL if errors were encountered. 193 Return: Caller must CFRelease() the return value when done. 194***/ 195CFTypeRef IOPSCopyPowerSourcesInfo(void) { 196 SCDynamicStoreRef store = NULL; 197 CFArrayRef ps_arr = NULL; 198 CFDictionaryRef power_sources = NULL; 199 200 // Open connection to SCDynamicStore 201 store = SCDynamicStoreCreate(kCFAllocatorDefault, 202 CFSTR("IOKit Power Source Copy"), NULL, NULL); 203 if(!store) { 204 goto exit; 205 } 206 207 ps_arr = CreatePSKeysArray(); 208 209#if TARGET_OS_EMBEDDED 210 // No need to pattern check on embedded. There is always only one power source 211 // <rdar://problem/11177160> 212 power_sources = SCDynamicStoreCopyMultiple(store, ps_arr, NULL); 213#else 214 // Copy multiple Power Sources into dictionary 215 power_sources = SCDynamicStoreCopyMultiple(store, NULL, ps_arr); 216#endif 217 218exit: 219 220 if (ps_arr) 221 CFRelease(ps_arr); 222 if (store) 223 CFRelease(store); 224 225 if(!power_sources) { 226 // On failure, we return an empty dictionary instead of NULL 227 power_sources = CFDictionaryCreate( kCFAllocatorDefault, 228 NULL, NULL, 0, 229 &kCFTypeDictionaryKeyCallBacks, 230 &kCFTypeDictionaryValueCallBacks); 231 } 232 233 // Return CFDictionary as opaque CFTypeRef 234 return (CFTypeRef)power_sources; 235} 236 237/*** 238 Arguments - Takes the CFTypeRef returned by IOPSCopyPowerSourcesInfo() 239 Returns a CFArray of Power Source handles, each of type CFTypeRef. 240 The caller shouldn't look directly at the CFTypeRefs, but should use 241 IOPSGetPowerSourceDescription on each member of the CFArray. 242 Returns NULL if errors were encountered. 243 Return: Caller must CFRelease() the returned CFArray. 244***/ 245CFArrayRef IOPSCopyPowerSourcesList(CFTypeRef blob) { 246 int count; 247 void **keys; 248 CFArrayRef arr; 249 bool failure = false; 250 251 // Check that the argument is actually a CFDictionary 252 if( !blob 253 || (CFGetTypeID(blob) != CFDictionaryGetTypeID()) ) 254 { 255 failure = true; 256 goto exit; 257 } 258 259 // allocate buffers for keys and values 260 count = CFDictionaryGetCount((CFDictionaryRef)blob); 261 keys = (void **)malloc(count * sizeof(void *)); 262 if(!keys) { 263 failure = true; 264 goto exit; 265 } 266 267 // Get keys and values from CFDictionary 268 CFDictionaryGetKeysAndValues((CFDictionaryRef)blob, (const void **)keys, NULL); 269 270 // Create CFArray from keys 271 arr = CFArrayCreate(kCFAllocatorDefault, (const void **)keys, count, &kCFTypeArrayCallBacks); 272 273 // free keys and values 274 free(keys); 275exit: 276 if(failure) { 277 // On failure, we return an empty array instead of NULL 278 arr = CFArrayCreate( 0, NULL, 0, &kCFTypeArrayCallBacks); 279 } 280 // Return CFArray 281 return arr; 282} 283 284/*** 285 Arguments - Takes one of the CFTypeRefs in the CFArray returned by 286 IOPSCopyPowerSourcesList 287 Returns a CFDictionary with specific information about the power source. 288 See IOKit.framework/Headers/ups/IOUPSKeys.h for specific fields. 289 Return: Caller should not CFRelease the returned CFArray 290***/ 291CFDictionaryRef IOPSGetPowerSourceDescription(CFTypeRef blob, CFTypeRef ps) { 292 // Check that the info is a CFDictionary 293 if( !(blob && (CFGetTypeID(blob)==CFDictionaryGetTypeID())) ) 294 return NULL; 295 296 // Check that the Power Source is a CFString 297 if( !(ps && (CFGetTypeID(ps)==CFStringGetTypeID())) ) 298 return NULL; 299 300 // Extract the CFDictionary of Battery Info 301 // and return 302 return CFDictionaryGetValue(blob, ps); 303} 304 305static CFStringRef getPowerSourceState(CFTypeRef blob, CFTypeRef id) 306{ 307 CFDictionaryRef the_dict = IOPSGetPowerSourceDescription(blob, id); 308 return CFDictionaryGetValue(the_dict, CFSTR(kIOPSPowerSourceStateKey)); 309} 310 311/* IOPSGetProvidingPowerSourceType 312 * Argument: 313 * ps_blob: as returned from IOPSCopyPowerSourcesInfo() 314 * Returns: 315 * The current system power source. 316 * CFSTR("AC Power"), CFSTR("Battery Power"), CFSTR("UPS Power") 317 */ 318CFStringRef IOPSGetProvidingPowerSourceType(CFTypeRef ps_blob) 319{ 320 CFTypeRef the_ups = NULL; 321 CFTypeRef the_batt = NULL; 322 CFStringRef ps_state = NULL; 323 324 325 if(kCFBooleanFalse == IOPSPowerSourceSupported(ps_blob, CFSTR(kIOPMBatteryPowerKey))) 326 { 327 if(kCFBooleanFalse == IOPSPowerSourceSupported(ps_blob, CFSTR(kIOPMUPSPowerKey))) { 328 // no batteries, no UPS -> AC Power 329 return CFSTR(kIOPMACPowerKey); 330 } else { 331 // optimization opportunity: needless loops inside IOPSGetActiveUPS 332 the_ups = IOPSGetActiveUPS(ps_blob); 333 if(!the_ups) return CFSTR(kIOPMACPowerKey); 334 ps_state = getPowerSourceState(ps_blob, the_ups); 335 if(ps_state && CFEqual(ps_state, CFSTR(kIOPSACPowerValue))) 336 { 337 // no batteries, yes UPS, UPS is running off of AC power -> AC Power 338 return CFSTR(kIOPMACPowerKey); 339 } else if(ps_state && CFEqual(ps_state, CFSTR(kIOPSBatteryPowerValue))) 340 { 341 // no batteries, yes UPS, UPS is running off of Battery power -> UPS Power 342 return CFSTR(kIOPMUPSPowerKey); 343 } 344 345 } 346 // Error in the data we were passed 347 return CFSTR(kIOPMACPowerKey); 348 } else { 349 // Optimization opportunity: needless loops inside IOPSGetActiveBattery 350 the_batt = IOPSGetActiveBattery(ps_blob); 351 if(!the_batt) return CFSTR(kIOPMACPowerKey); 352 ps_state = getPowerSourceState(ps_blob, the_batt); 353 if(ps_state && CFEqual(ps_state, CFSTR(kIOPSBatteryPowerValue))) 354 { 355 // Yes batteries, yes running on battery power -> Battery power 356 return CFSTR(kIOPMBatteryPowerKey); 357 } else { 358 // batteries are on AC power. let's check UPS. 359 // optimize. 360 if(kCFBooleanFalse == IOPSPowerSourceSupported(ps_blob, CFSTR(kIOPMUPSPowerKey))) 361 { 362 // yes batteries on AC power, no UPS -> AC Power 363 return CFSTR(kIOPMACPowerKey); 364 } else { 365 the_ups = IOPSGetActiveUPS(ps_blob); 366 if(!the_ups) return CFSTR(kIOPMACPowerKey); 367 ps_state = getPowerSourceState(ps_blob, the_ups); 368 if(ps_state && CFEqual(ps_state, CFSTR(kIOPSBatteryPowerValue))) 369 { 370 // yes batteries on AC power, UPS is on battery power -> UPS Power 371 return CFSTR(kIOPMUPSPowerKey); 372 } else if(ps_state && CFEqual(ps_state, CFSTR(kIOPSACPowerValue))) 373 { 374 // yes batteries on AC Power, UPS is on AC Power -> AC Power 375 return CFSTR(kIOPMACPowerKey); 376 } 377 } 378 } 379 } 380 381 // Should not reach this point. Return something safe. 382 return CFSTR(kIOPMACPowerKey); 383} 384 385 386/*** 387 Support structures and functions for IOPSNotificationCreateRunLoopSource 388***/ 389typedef struct { 390 IOPowerSourceCallbackType callback; 391 void *context; 392 int token; 393 CFMachPortRef mpRef; 394} IOPSNotifyCallbackContext; 395 396static void IOPSRLSMachPortCallback (CFMachPortRef port __unused, void *msg __unused, CFIndex size __unused, void *info) 397{ 398 IOPSNotifyCallbackContext *c = (IOPSNotifyCallbackContext *)info; 399 IOPowerSourceCallbackType cb; 400 401 if (c && (cb = c->callback)) { 402 (*cb)(c->context); 403 } 404} 405 406static void IOPSRLSMachPortRelease(const void *info) 407{ 408 IOPSNotifyCallbackContext *c = (IOPSNotifyCallbackContext *)info; 409 410 if (c) { 411 if (0 != c->token) { 412 notify_cancel(c->token); 413 } 414 if (c->mpRef) { 415 CFMachPortInvalidate(c->mpRef); 416 CFRelease(c->mpRef); 417 } 418 free(c); 419 } 420} 421 422 423static CFRunLoopSourceRef doCreatePSRLS(const char *notify_type, IOPowerSourceCallbackType callback, void *context) 424{ 425 int status = 0; 426 int token = 0; 427 mach_port_t mp = MACH_PORT_NULL; 428 CFMachPortRef mpRef = NULL; 429 CFMachPortContext mpContext; 430 CFRunLoopSourceRef mpRLS = NULL; 431 IOPSNotifyCallbackContext *ioContext; 432 Boolean isReused = false; 433 int giveUpRetryCount = 5; 434 435 status = notify_register_mach_port(notify_type, &mp, 0, &token); 436 if (NOTIFY_STATUS_OK != status) { 437 return NULL; 438 } 439 440 ioContext = calloc(1, sizeof(IOPSNotifyCallbackContext)); 441 ioContext->callback = callback; 442 ioContext->context = context; 443 ioContext->token = token; 444 445 bzero(&mpContext, sizeof(mpContext)); 446 mpContext.info = (void *)ioContext; 447 mpContext.release = IOPSRLSMachPortRelease; 448 449 do { 450 if (mpRef) { 451 // CFMachPorts may be reused. We don't want to get a reused mach port; so if we're unlucky enough 452 // to get one, we'll pre-emptively invalidate it, throw them back in the pool, and retry. 453 CFMachPortInvalidate(mpRef); 454 CFRelease(mpRef); 455 } 456 457 mpRef = CFMachPortCreateWithPort(0, mp, IOPSRLSMachPortCallback, &mpContext, &isReused); 458 } while (!mpRef && isReused && (--giveUpRetryCount > 0)); 459 460 if (mpRef) { 461 if (!isReused) { 462 // A reused mach port is a failure; it'll have an invalid callback pointer associated with it. 463 ioContext->mpRef = mpRef; 464 mpRLS = CFMachPortCreateRunLoopSource(0, mpRef, 0); 465 } 466 CFRelease(mpRef); 467 } 468 469 return mpRLS; 470} 471 472CFRunLoopSourceRef IOPSNotificationCreateRunLoopSource(IOPowerSourceCallbackType callback, void *context) { 473 return doCreatePSRLS(kIOPSNotifyTimeRemaining, callback, context); 474} 475 476CFRunLoopSourceRef IOPSCreateLimitedPowerNotification(IOPowerSourceCallbackType callback, void *context) { 477 return doCreatePSRLS(kIOPSNotifyPowerSource, callback, context); 478} 479 480