AutoWakeScheduler.c [plain text]
#include <CoreFoundation/CoreFoundation.h>
#include <SystemConfiguration/SystemConfiguration.h>
#include <SystemConfiguration/SCValidation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/IOMessage.h>
#include <IOKit/hidsystem/IOHIDLib.h>
#include <IOKit/graphics/IOGraphicsTypes.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <IOKit/pwr_mgt/IOPMLib.h>
#include "PrivateLib.h"
#include "AutoWakeScheduler.h"
#include "RepeatingAutoWake.h"
static CFMutableArrayRef wakeup_arr = 0;
static CFMutableArrayRef poweron_arr = 0;
static CFMutableArrayRef sleep_arr = 0;
static CFMutableArrayRef shutdown_arr = 0;
static CFRunLoopTimerRef wakeup_timer = 0;
static CFRunLoopTimerRef poweron_timer = 0;
static CFRunLoopTimerRef sleep_timer = 0;
static CFRunLoopTimerRef shutdown_timer = 0;
enum {
kIOWakeTimer = 0,
kIOPowerOnTimer = 1,
kIOSleepTimer = 2,
kIOShutdownTimer = 3
};
static bool
isEntryValidAndFuturistic(CFDictionaryRef wakeup_dict, CFDateRef date_now)
{
CFDateRef wakeup_date;
bool ret = true;
wakeup_dict = isA_CFDictionary(wakeup_dict);
if(!wakeup_dict)
{
ret = false;
} else
{
wakeup_date = isA_CFDate(CFDictionaryGetValue(wakeup_dict, CFSTR(kIOPMPowerEventTimeKey)));
if(!wakeup_date || (kCFCompareLessThan == CFDateCompare(wakeup_date, date_now, 0)))
{
ret = false;
}
}
return ret;
}
static bool
purgePastWakeupTimes(CFMutableArrayRef arr, CFStringRef which)
{
int i;
bool array_has_changed = false;
CFDateRef date_now;
bool ret;
SCPreferencesRef prefs = 0;
if(!arr || (0 == CFArrayGetCount(arr))) return true;
i = 0;
date_now = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
do {
if(!isEntryValidAndFuturistic(CFArrayGetValueAtIndex(arr, i), date_now))
{
CFArrayRemoveValueAtIndex(arr, i);
array_has_changed = true;
} else
{
break;
}
} while(i < CFArrayGetCount(arr));
CFRelease(date_now);
if(array_has_changed)
{
prefs = SCPreferencesCreate(0, CFSTR("IOKit-AutoWake"), CFSTR(kIOPMAutoWakePrefsPath));
if(!prefs) return 0;
if(!SCPreferencesLock(prefs, true)) {
ret = false;
goto exit;
}
SCPreferencesSetValue(prefs, which, arr);
if(!SCPreferencesCommitChanges(prefs)) {
ret = false;
goto exit;
}
SCPreferencesUnlock(prefs);
}
ret = true;
exit:
if(prefs) CFRelease(prefs);
return ret;
}
static void
copyScheduledPowerChangeArrays(void)
{
CFArrayRef tmp;
SCPreferencesRef prefs;
prefs = SCPreferencesCreate(0, CFSTR("PM-configd-AutoWake"), CFSTR(kIOPMAutoWakePrefsPath));
if(!prefs) return;
tmp = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoWake)));
if(tmp) wakeup_arr = CFArrayCreateMutableCopy(0, 0, tmp);
tmp = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoPowerOn)));
if(tmp) poweron_arr = CFArrayCreateMutableCopy(0, 0, tmp);
tmp = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoSleep)));
if(tmp) sleep_arr = CFArrayCreateMutableCopy(0, 0, tmp);
tmp = isA_CFArray(SCPreferencesGetValue(prefs, CFSTR(kIOPMAutoShutdown)));
if(tmp) shutdown_arr = CFArrayCreateMutableCopy(0, 0, tmp);
CFRelease(prefs);
}
static CFDictionaryRef
findEarliestUpcomingTime(CFMutableArrayRef arr)
{
int i=0;
int count;
if(!arr) return NULL;
count = CFArrayGetCount(arr);
CFDateRef now = CFDateCreate(0, CFAbsoluteTimeGetCurrent() + 10.0);
while(i<count && !isEntryValidAndFuturistic(CFArrayGetValueAtIndex(arr, i), now))
{
i++;
}
CFRelease(now);
if(i < count)
return CFArrayGetValueAtIndex(arr, i);
else return NULL;
}
static bool
tellSettingsController(CFDictionaryRef dat, CFStringRef command)
{
CFAbsoluteTime now, wake_time;
long int diff_secs;
IOReturn ret;
bool return_val = false;
CFNumberRef seconds_delta = NULL;
if(!command) goto exit;
if(!dat) {
diff_secs = 0;
} else {
now = CFAbsoluteTimeGetCurrent();
if(dat)
wake_time = CFDateGetAbsoluteTime(CFDictionaryGetValue(dat, CFSTR(kIOPMPowerEventTimeKey)));
else wake_time = now;
diff_secs = lround(wake_time - now);
if(diff_secs < 0) goto exit;
}
seconds_delta = CFNumberCreate(0, kCFNumberLongType, &diff_secs);
if(!seconds_delta) goto exit;
ret = _setRootDomainProperty(command, seconds_delta);
if(kIOReturnSuccess != ret)
{
return_val = false;
goto exit;
}
return_val = true;
exit:
if(seconds_delta) CFRelease(seconds_delta);
return return_val;
}
static kern_return_t openHIDService(mach_port_t io_master_port, io_connect_t *connection)
{
kern_return_t kr;
mach_port_t ev, service, iter;
kr = IOServiceGetMatchingServices(io_master_port, IOServiceMatching(kIOHIDSystemClass), &iter);
if (kr != KERN_SUCCESS) {
return kr;
}
service = IOIteratorNext(iter);
kr = IOServiceOpen(service, mach_task_self(), kIOHIDParamConnectType, &ev);
IOObjectRelease(service);
IOObjectRelease(iter);
if (kr != KERN_SUCCESS) {
return kr;
}
*connection = ev;
return kr;
}
static void wakeDozingMachine(void)
{
IOGPoint loc;
kern_return_t kr;
NXEvent nullEvent = {NX_NULLEVENT, {0, 0}, 0, -1, 0};
static io_connect_t io_connection = MACH_PORT_NULL;
if (io_connection == MACH_PORT_NULL)
{
kr = openHIDService(0, &io_connection);
if (kr != KERN_SUCCESS)
{
io_connection = NULL;
return;
}
}
IOHIDPostEvent(io_connection, NX_NULLEVENT, loc, &nullEvent.data, FALSE, 0, FALSE);
return;
}
static bool isRepeating(CFDictionaryRef event)
{
CFStringRef whose = CFDictionaryGetValue(event, CFSTR(kIOPMPowerEventAppNameKey));
if(kCFCompareEqualTo == CFStringCompare(whose, CFSTR("Repeating"), 0))
return true;
else
return false;
}
static CFStringRef
_getScheduledEventType(CFDictionaryRef event)
{
return isA_CFString(CFDictionaryGetValue(event, CFSTR(kIOPMPowerEventTypeKey)));
}
static void
_setScheduledEventType(CFMutableDictionaryRef event, CFStringRef type)
{
CFDictionarySetValue(event, CFSTR(kIOPMPowerEventTypeKey), type);
}
static void schedulePowerOnTime(void);
static void scheduleWakeTime(void);
static void
handleTimerPowerOnReset(CFRunLoopTimerRef blah, void *info)
{
CFDictionaryRef event = (CFDictionaryRef)info;
CFStringRef type;
type = isA_CFString(CFDictionaryGetValue(event, CFSTR(kIOPMPowerEventTypeKey)));
if(!type) return;
if(kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPMAutoWake), 0))
{
wakeup_timer = 0;
wakeDozingMachine();
if(isRepeating(event))
{
RepeatingAutoWakeTimeForPowerOn();
}
} else if(kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPMAutoPowerOn), 0))
{
poweron_timer = 0;
schedulePowerOnTime();
if(isRepeating(event))
{
RepeatingAutoWakeTimeForPowerOn();
}
} else if(kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPMAutoSleep), 0))
{
sleep_timer = 0;
_askNicelyThenSleepSystem();
if(isRepeating(event))
{
RepeatingAutoWakeTimeForPowerOff();
}
} else if(kCFCompareEqualTo == CFStringCompare(type, CFSTR(kIOPMAutoShutdown), 0))
{
shutdown_timer = 0;
_askNicelyThenShutdownSystem();
if(isRepeating(event))
{
RepeatingAutoWakeTimeForPowerOff();
}
}
}
static void
schedulePowerOnTime(void)
{
static CFRunLoopTimerContext tmr_context = {0, 0, CFRetain, CFRelease, 0};
CFAbsoluteTime fire_time;
CFDictionaryRef temp_upcoming;
CFMutableDictionaryRef upcoming;
CFStringRef event_type;
if(poweron_timer)
{
CFRunLoopTimerInvalidate(poweron_timer);
poweron_timer = 0;
}
temp_upcoming = findEarliestUpcomingTime(poweron_arr);
if(!temp_upcoming)
{
return;
}
upcoming = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, temp_upcoming);
if(!upcoming) return;
event_type = _getScheduledEventType(upcoming);
if(event_type && (kCFCompareEqualTo == CFStringCompare(event_type, CFSTR(kIOPMAutoWakeOrPowerOn), 0)) );
{
_setScheduledEventType(upcoming, CFSTR(kIOPMAutoPowerOn));
}
tmr_context.info = (void *)upcoming;
fire_time = CFDateGetAbsoluteTime(CFDictionaryGetValue(upcoming, CFSTR(kIOPMPowerEventTimeKey)));
poweron_timer = CFRunLoopTimerCreate(0, fire_time, 0.0, 0,
0, handleTimerPowerOnReset, &tmr_context);
if(poweron_timer)
{
CFRunLoopAddTimer(CFRunLoopGetCurrent(), poweron_timer, kCFRunLoopDefaultMode);
CFRelease(poweron_timer);
}
tellSettingsController(upcoming, CFSTR(kIOPMAutoPowerOn));
CFRelease(upcoming);
}
static void
scheduleWakeTime(void)
{
static CFRunLoopTimerContext tmr_context = {0, 0, CFRetain, CFRelease, 0};
CFAbsoluteTime fire_time;
CFDictionaryRef temp_upcoming;
CFMutableDictionaryRef upcoming;
CFStringRef event_type;
if(wakeup_timer)
{
CFRunLoopTimerInvalidate(wakeup_timer);
wakeup_timer = 0;
}
temp_upcoming = findEarliestUpcomingTime(wakeup_arr);
if(!temp_upcoming)
{
return;
}
upcoming = CFDictionaryCreateMutableCopy(kCFAllocatorDefault, 0, temp_upcoming);
if(!upcoming) return;
event_type = _getScheduledEventType(upcoming);
if(event_type && (kCFCompareEqualTo == CFStringCompare(event_type, CFSTR(kIOPMAutoWakeOrPowerOn), 0)) );
{
_setScheduledEventType(upcoming, CFSTR(kIOPMAutoWake));
}
tmr_context.info = (void *)upcoming;
tellSettingsController(upcoming, CFSTR(kIOPMAutoWake));
fire_time = CFDateGetAbsoluteTime(CFDictionaryGetValue(upcoming, CFSTR(kIOPMPowerEventTimeKey)));
wakeup_timer = CFRunLoopTimerCreate(0, fire_time, 0.0, 0,
0, handleTimerPowerOnReset, &tmr_context);
if(wakeup_timer)
{
CFRunLoopAddTimer(CFRunLoopGetCurrent(), wakeup_timer, kCFRunLoopDefaultMode);
CFRelease(wakeup_timer);
}
CFRelease(upcoming);
}
static void
scheduleSleepTime(void)
{
static CFRunLoopTimerContext tmr_context = {0, 0, CFRetain, CFRelease, 0};
CFAbsoluteTime fire_time;
CFDictionaryRef upcoming;
if(sleep_timer)
{
CFRunLoopTimerInvalidate(sleep_timer);
sleep_timer = 0;
}
upcoming = findEarliestUpcomingTime(sleep_arr);
if(!upcoming)
{
return;
}
tmr_context.info = (void *)upcoming;
fire_time = CFDateGetAbsoluteTime(CFDictionaryGetValue(upcoming, CFSTR(kIOPMPowerEventTimeKey)));
sleep_timer = CFRunLoopTimerCreate(0, fire_time, 0.0, 0,
0, handleTimerPowerOnReset, &tmr_context);
if(sleep_timer)
{
CFRunLoopAddTimer(CFRunLoopGetCurrent(), sleep_timer, kCFRunLoopDefaultMode);
CFRelease(sleep_timer);
}
}
static void
scheduleShutdownTime(void)
{
static CFRunLoopTimerContext tmr_context = {0, 0, CFRetain, CFRelease, 0};
CFAbsoluteTime fire_time;
CFDictionaryRef upcoming;
if(shutdown_timer)
{
CFRunLoopTimerInvalidate(shutdown_timer);
shutdown_timer = 0;
}
upcoming = findEarliestUpcomingTime(shutdown_arr);
if(!upcoming)
{
return;
}
tmr_context.info = (void *)upcoming;
fire_time = CFDateGetAbsoluteTime(CFDictionaryGetValue(upcoming, CFSTR(kIOPMPowerEventTimeKey)));
shutdown_timer = CFRunLoopTimerCreate(0, fire_time, 0.0, 0,
0, handleTimerPowerOnReset, &tmr_context);
if(shutdown_timer)
{
CFRunLoopAddTimer(CFRunLoopGetCurrent(), shutdown_timer, kCFRunLoopDefaultMode);
CFRelease(shutdown_timer);
}
}
__private_extern__ void
AutoWake_prime(void)
{
copyScheduledPowerChangeArrays();
purgePastWakeupTimes(wakeup_arr, CFSTR(kIOPMAutoWake));
purgePastWakeupTimes(poweron_arr, CFSTR(kIOPMAutoPowerOn));
purgePastWakeupTimes(sleep_arr, CFSTR(kIOPMAutoSleep));
purgePastWakeupTimes(shutdown_arr, CFSTR(kIOPMAutoShutdown));
scheduleWakeTime();
schedulePowerOnTime();
scheduleSleepTime();
scheduleShutdownTime();
}
__private_extern__ void
AutoWakeSleepWakeNotification(natural_t message_type)
{
switch (message_type) {
case kIOMessageSystemWillSleep:
scheduleWakeTime();
break;
case kIOMessageSystemHasPoweredOn:
purgePastWakeupTimes(wakeup_arr, CFSTR(kIOPMAutoWake));
purgePastWakeupTimes(poweron_arr, CFSTR(kIOPMAutoPowerOn));
purgePastWakeupTimes(sleep_arr, CFSTR(kIOPMAutoSleep));
purgePastWakeupTimes(shutdown_arr, CFSTR(kIOPMAutoShutdown));
break;
default:
break;
}
}
__private_extern__ void
AutoWakePrefsHaveChanged(void)
{
if(wakeup_arr)
{
CFRelease(wakeup_arr);
wakeup_arr = 0;
}
if(poweron_arr)
{
CFRelease(poweron_arr);
poweron_arr = 0;
}
if(sleep_arr)
{
CFRelease(sleep_arr);
sleep_arr = 0;
}
if(shutdown_arr)
{
CFRelease(shutdown_arr);
shutdown_arr = 0;
}
copyScheduledPowerChangeArrays();
tellSettingsController(NULL, CFSTR(kIOPMAutoWake));
tellSettingsController(NULL, CFSTR(kIOPMAutoPowerOn));
scheduleWakeTime();
schedulePowerOnTime();
scheduleSleepTime();
scheduleShutdownTime();
}
__private_extern__ void
AutoWakePMUInterestNotification(natural_t messageType, UInt32 messageArgument)
{
if(0 == messageArgument) return;
if(kIOPMUMessageLegacyAutoWake == messageType)
{
CFDateRef wakey_date;
CFAbsoluteTime wakey_time;
wakey_time = CFAbsoluteTimeGetCurrent() + (CFAbsoluteTime)messageArgument;
wakey_date = CFDateCreate(kCFAllocatorDefault, wakey_time);
IOPMSchedulePowerEvent(wakey_date, CFSTR("Legacy PMU setProperties"), CFSTR(kIOPMAutoWake));
CFRelease(wakey_date);
} else if(kIOPMUMessageLegacyAutoPower == messageType)
{
CFDateRef wakey_date;
CFAbsoluteTime wakey_time;
wakey_time = CFAbsoluteTimeGetCurrent() + (CFAbsoluteTime)messageArgument;
wakey_date = CFDateCreate(kCFAllocatorDefault, wakey_time);
IOPMSchedulePowerEvent(wakey_date, CFSTR("Legacy PMU setProperties"), CFSTR(kIOPMAutoPowerOn));
CFRelease(wakey_date);
}
}