#include <sys/cdefs.h>
#include <notify.h>
#include <CoreFoundation/CoreFoundation.h>
#include "IOSystemConfiguration.h"
#include "IOPSKeys.h"
#include "IOPowerSources.h"
#include "IOPowerSourcesPrivate.h"
#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
#include <notify.h>
#ifndef kIOPSDynamicStorePathKey
#define kIOPSDynamicStorePathKey kIOPSDynamicStorePath
#endif
#ifndef kIOPSDynamicStoreLowBattPathKey
#define kIOPSDynamicStoreLowBattPathKey "/IOKit/LowBatteryWarning"
#endif
#ifndef kIOPSDynamicStorePowerAdapterKey
#define kIOPSDynamicStorePowerAdapterKey "/IOKit/PowerAdapter"
#endif
#if TARGET_OS_EMBEDDED
#define kIOPSDynamicStoreFullPath "State:/IOKit/PowerSources/InternalBattery-0"
#endif
IOPSLowBatteryWarningLevel IOPSGetBatteryWarningLevel(void)
{
SCDynamicStoreRef store = NULL;
CFStringRef key = NULL;
CFNumberRef scWarnValue = NULL;
int return_level = kIOPSLowBatteryWarningNone;
store = SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("IOKit Power Source Copy"), NULL, NULL);
if (!store)
goto SAD_EXIT;
key = SCDynamicStoreKeyCreate(
kCFAllocatorDefault,
CFSTR("%@%@"),
_io_kSCDynamicStoreDomainState,
CFSTR(kIOPSDynamicStoreLowBattPathKey));
if (!key)
goto SAD_EXIT;
scWarnValue = isA_CFNumber(SCDynamicStoreCopyValue(store, key));
if (scWarnValue) {
CFNumberGetValue(scWarnValue, kCFNumberIntType, &return_level);
CFRelease(scWarnValue);
scWarnValue = NULL;
}
SAD_EXIT:
if (store) CFRelease(store);
if (key) CFRelease(key);
return return_level;
}
#define _kPSTimeRemainingNotifyExternalBit (1 << 16)
#define _kPSTimeRemainingNotifyChargingBit (1 << 17)
#define _kPSTimeRemainingNotifyUnknownBit (1 << 18)
#define _kPSTimeRemainingNotifyValidBit (1 << 19)
#define _kSecondsPerMinute ((CFTimeInterval)60.0)
CFTimeInterval IOPSGetTimeRemainingEstimate(void)
{
int myNotifyToken = 0;
uint64_t packedBatteryData = 0;
int myNotifyStatus = 0;
myNotifyStatus = notify_register_check(kIOPSTimeRemainingNotificationKey, &myNotifyToken);
if (NOTIFY_STATUS_OK != myNotifyStatus) {
return kIOPSTimeRemainingUnlimited;
}
notify_get_state(myNotifyToken, &packedBatteryData);
notify_cancel(myNotifyToken);
if (!(packedBatteryData & _kPSTimeRemainingNotifyValidBit)
|| (packedBatteryData & _kPSTimeRemainingNotifyExternalBit)) {
return kIOPSTimeRemainingUnlimited;
}
if (packedBatteryData & _kPSTimeRemainingNotifyUnknownBit) {
return kIOPSTimeRemainingUnknown;
}
return (_kSecondsPerMinute * (CFTimeInterval)(packedBatteryData & 0xFFFF));
}
CFDictionaryRef IOPSCopyExternalPowerAdapterDetails(void)
{
SCDynamicStoreRef store = NULL;
CFStringRef key = NULL;
CFDictionaryRef ret_dict = NULL;
store = SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("IOKit Power Source Copy"), NULL, NULL);
if (!store)
goto SAD_EXIT;
key = SCDynamicStoreKeyCreate(
kCFAllocatorDefault,
CFSTR("%@%@"),
_io_kSCDynamicStoreDomainState,
CFSTR(kIOPSDynamicStorePowerAdapterKey));
if (!key)
goto SAD_EXIT;
ret_dict = isA_CFDictionary(SCDynamicStoreCopyValue(store, key));
SAD_EXIT:
if (store) CFRelease(store);
if (key) CFRelease(key);
return ret_dict;
}
static CFArrayRef CreatePSKeysArray(void)
{
CFStringRef ps_match = NULL;
CFMutableArrayRef ps_arr = NULL;
#if TARGET_OS_EMBEDDED
ps_match = SCDynamicStoreKeyCreate(kCFAllocatorDefault,
CFSTR(kIOPSDynamicStoreFullPath));
#else
ps_match = SCDynamicStoreKeyCreate(
kCFAllocatorDefault,
CFSTR("%@%@/%@"),
_io_kSCDynamicStoreDomainState,
CFSTR(kIOPSDynamicStorePath),
_io_kSCCompAnyRegex);
#endif
if(!ps_match) return NULL;
ps_arr = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
if(!ps_arr) return NULL;
CFArrayAppendValue(ps_arr, ps_match);
CFRelease(ps_match);
return (CFArrayRef)ps_arr;
}
CFTypeRef IOPSCopyPowerSourcesInfo(void) {
SCDynamicStoreRef store = NULL;
CFArrayRef ps_arr = NULL;
CFDictionaryRef power_sources = NULL;
store = SCDynamicStoreCreate(kCFAllocatorDefault,
CFSTR("IOKit Power Source Copy"), NULL, NULL);
if(!store) {
goto exit;
}
ps_arr = CreatePSKeysArray();
#if TARGET_OS_EMBEDDED
power_sources = SCDynamicStoreCopyMultiple(store, ps_arr, NULL);
#else
power_sources = SCDynamicStoreCopyMultiple(store, NULL, ps_arr);
#endif
exit:
if (ps_arr)
CFRelease(ps_arr);
if (store)
CFRelease(store);
if(!power_sources) {
power_sources = CFDictionaryCreate( kCFAllocatorDefault,
NULL, NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
return (CFTypeRef)power_sources;
}
CFArrayRef IOPSCopyPowerSourcesList(CFTypeRef blob) {
int count;
void **keys;
CFArrayRef arr;
bool failure = false;
if( !blob
|| (CFGetTypeID(blob) != CFDictionaryGetTypeID()) )
{
failure = true;
goto exit;
}
count = CFDictionaryGetCount((CFDictionaryRef)blob);
keys = (void **)malloc(count * sizeof(void *));
if(!keys) {
failure = true;
goto exit;
}
CFDictionaryGetKeysAndValues((CFDictionaryRef)blob, (const void **)keys, NULL);
arr = CFArrayCreate(kCFAllocatorDefault, (const void **)keys, count, &kCFTypeArrayCallBacks);
free(keys);
exit:
if(failure) {
arr = CFArrayCreate( 0, NULL, 0, &kCFTypeArrayCallBacks);
}
return arr;
}
CFDictionaryRef IOPSGetPowerSourceDescription(CFTypeRef blob, CFTypeRef ps) {
if( !(blob && (CFGetTypeID(blob)==CFDictionaryGetTypeID())) )
return NULL;
if( !(ps && (CFGetTypeID(ps)==CFStringGetTypeID())) )
return NULL;
return CFDictionaryGetValue(blob, ps);
}
static CFStringRef getPowerSourceState(CFTypeRef blob, CFTypeRef id)
{
CFDictionaryRef the_dict = IOPSGetPowerSourceDescription(blob, id);
return CFDictionaryGetValue(the_dict, CFSTR(kIOPSPowerSourceStateKey));
}
CFStringRef IOPSGetProvidingPowerSourceType(CFTypeRef ps_blob)
{
CFTypeRef the_ups = NULL;
CFTypeRef the_batt = NULL;
CFStringRef ps_state = NULL;
if(kCFBooleanFalse == IOPSPowerSourceSupported(ps_blob, CFSTR(kIOPMBatteryPowerKey)))
{
if(kCFBooleanFalse == IOPSPowerSourceSupported(ps_blob, CFSTR(kIOPMUPSPowerKey))) {
return CFSTR(kIOPMACPowerKey);
} else {
the_ups = IOPSGetActiveUPS(ps_blob);
if(!the_ups) return CFSTR(kIOPMACPowerKey);
ps_state = getPowerSourceState(ps_blob, the_ups);
if(ps_state && CFEqual(ps_state, CFSTR(kIOPSACPowerValue)))
{
return CFSTR(kIOPMACPowerKey);
} else if(ps_state && CFEqual(ps_state, CFSTR(kIOPSBatteryPowerValue)))
{
return CFSTR(kIOPMUPSPowerKey);
}
}
return CFSTR(kIOPMACPowerKey);
} else {
the_batt = IOPSGetActiveBattery(ps_blob);
if(!the_batt) return CFSTR(kIOPMACPowerKey);
ps_state = getPowerSourceState(ps_blob, the_batt);
if(ps_state && CFEqual(ps_state, CFSTR(kIOPSBatteryPowerValue)))
{
return CFSTR(kIOPMBatteryPowerKey);
} else {
if(kCFBooleanFalse == IOPSPowerSourceSupported(ps_blob, CFSTR(kIOPMUPSPowerKey)))
{
return CFSTR(kIOPMACPowerKey);
} else {
the_ups = IOPSGetActiveUPS(ps_blob);
if(!the_ups) return CFSTR(kIOPMACPowerKey);
ps_state = getPowerSourceState(ps_blob, the_ups);
if(ps_state && CFEqual(ps_state, CFSTR(kIOPSBatteryPowerValue)))
{
return CFSTR(kIOPMUPSPowerKey);
} else if(ps_state && CFEqual(ps_state, CFSTR(kIOPSACPowerValue)))
{
return CFSTR(kIOPMACPowerKey);
}
}
}
}
return CFSTR(kIOPMACPowerKey);
}
typedef struct {
IOPowerSourceCallbackType callback;
void *context;
int token;
CFMachPortRef mpRef;
} IOPSNotifyCallbackContext;
static void IOPSRLSMachPortCallback (CFMachPortRef port __unused, void *msg __unused, CFIndex size __unused, void *info)
{
IOPSNotifyCallbackContext *c = (IOPSNotifyCallbackContext *)info;
IOPowerSourceCallbackType cb;
if (c && (cb = c->callback)) {
(*cb)(c->context);
}
}
static void IOPSRLSMachPortRelease(const void *info)
{
IOPSNotifyCallbackContext *c = (IOPSNotifyCallbackContext *)info;
if (c) {
if (0 != c->token) {
notify_cancel(c->token);
}
if (c->mpRef) {
CFMachPortInvalidate(c->mpRef);
CFRelease(c->mpRef);
}
free(c);
}
}
static CFRunLoopSourceRef doCreatePSRLS(const char *notify_type, IOPowerSourceCallbackType callback, void *context)
{
int status = 0;
int token = 0;
mach_port_t mp = MACH_PORT_NULL;
CFMachPortRef mpRef = NULL;
CFMachPortContext mpContext;
CFRunLoopSourceRef mpRLS = NULL;
IOPSNotifyCallbackContext *ioContext;
Boolean isReused = false;
int giveUpRetryCount = 5;
status = notify_register_mach_port(notify_type, &mp, 0, &token);
if (NOTIFY_STATUS_OK != status) {
return NULL;
}
ioContext = calloc(1, sizeof(IOPSNotifyCallbackContext));
ioContext->callback = callback;
ioContext->context = context;
ioContext->token = token;
bzero(&mpContext, sizeof(mpContext));
mpContext.info = (void *)ioContext;
mpContext.release = IOPSRLSMachPortRelease;
do {
if (mpRef) {
CFMachPortInvalidate(mpRef);
CFRelease(mpRef);
}
mpRef = CFMachPortCreateWithPort(0, mp, IOPSRLSMachPortCallback, &mpContext, &isReused);
} while (!mpRef && isReused && (--giveUpRetryCount > 0));
if (mpRef) {
if (!isReused) {
ioContext->mpRef = mpRef;
mpRLS = CFMachPortCreateRunLoopSource(0, mpRef, 0);
}
CFRelease(mpRef);
}
return mpRLS;
}
CFRunLoopSourceRef IOPSNotificationCreateRunLoopSource(IOPowerSourceCallbackType callback, void *context) {
return doCreatePSRLS(kIOPSNotifyTimeRemaining, callback, context);
}
CFRunLoopSourceRef IOPSCreateLimitedPowerNotification(IOPowerSourceCallbackType callback, void *context) {
return doCreatePSRLS(kIOPSNotifyPowerSource, callback, context);
}