RepeatingAutoWake.c [plain text]
#include <syslog.h>
#include <syslog.h>
#include "RepeatingAutoWake.h"
#include "PrivateLib.h"
static CFDictionaryRef currentRepeatingEvents = 0;
static CFDictionaryRef newRepeatingEvents = 0;
static CFDictionaryRef repeatingPowerOff = 0;
static CFDictionaryRef repeatingPowerOn = 0;
static IOReturn
cancelAllRepeatingEvents(void)
{
CFArrayRef list = 0;
CFDictionaryRef scheduled_event;
CFDateRef scheduled_time;
CFStringRef scheduled_scheduler;
CFStringRef scheduled_type;
IOReturn ret = kIOReturnSuccess;
int i, count;
list = IOPMCopyScheduledPowerEvents();
if(!list || (0==CFArrayGetCount(list))) {
ret = kIOReturnSuccess;
goto exit;
}
count = CFArrayGetCount(list);
for(i=0; i<count; i++)
{
scheduled_event = CFArrayGetValueAtIndex(list, i);
if(!isA_CFDictionary(scheduled_event)) continue;
scheduled_scheduler = isA_CFString(CFDictionaryGetValue(scheduled_event,
CFSTR(kIOPMPowerEventAppNameKey)));
if( scheduled_scheduler
&& CFEqual(scheduled_scheduler, CFSTR("Repeating")) )
{
scheduled_time = CFDictionaryGetValue( scheduled_event,
CFSTR(kIOPMPowerEventTimeKey));
scheduled_type = CFDictionaryGetValue( scheduled_event,
CFSTR(kIOPMPowerEventTypeKey));
ret = IOPMCancelScheduledPowerEvent( scheduled_time,
scheduled_scheduler, scheduled_type);
if(kIOReturnSuccess!=ret)
{
}
}
}
exit:
if(list) CFRelease(list);
return ret;
}
static bool
is_valid_repeating_dictionary(CFDictionaryRef event)
{
CFNumberRef tmp_num;
CFStringRef tmp_str;
if(NULL == event) return true;
if(!isA_CFDictionary(event)) return false;
tmp_num = (CFNumberRef)CFDictionaryGetValue(event, CFSTR(kIOPMPowerEventTimeKey));
if(!isA_CFNumber(tmp_num)) return false;
tmp_num = (CFNumberRef)CFDictionaryGetValue(event, CFSTR(kIOPMDaysOfWeekKey));
if(!isA_CFNumber(tmp_num)) return false;
tmp_str = (CFStringRef)CFDictionaryGetValue(event, CFSTR(kIOPMPowerEventTypeKey));
if(!isA_CFString(tmp_str)) return false;
if( !CFEqual(tmp_str, CFSTR(kIOPMAutoSleep))
&& !CFEqual(tmp_str, CFSTR(kIOPMAutoShutdown))
&& !CFEqual(tmp_str, CFSTR(kIOPMAutoWakeOrPowerOn))
&& !CFEqual(tmp_str, CFSTR(kIOPMAutoPowerOn))
&& !CFEqual(tmp_str, CFSTR(kIOPMAutoWake))
&& !CFEqual(tmp_str, CFSTR(kIOPMAutoRestart)) )
{
return false;
}
return true;
}
static int
getRepeatingDictionaryMinutes(CFDictionaryRef event)
{
int val;
CFNumberRef tmp_num;
tmp_num = (CFNumberRef)CFDictionaryGetValue(event, CFSTR(kIOPMPowerEventTimeKey));
CFNumberGetValue(tmp_num, kCFNumberIntType, &val);
return val;
}
static int
getRepeatingDictionaryDayMask(CFDictionaryRef event)
{
int val;
CFNumberRef tmp_num;
tmp_num = (CFNumberRef)CFDictionaryGetValue(event, CFSTR(kIOPMDaysOfWeekKey));
CFNumberGetValue(tmp_num, kCFNumberIntType, &val);
return val;
}
static CFStringRef
getRepeatingDictionaryType(CFDictionaryRef event)
{
CFStringRef return_string;
if(!event) {
return CFSTR("");
}
return_string = isA_CFString( CFDictionaryGetValue(
event, CFSTR(kIOPMPowerEventTypeKey)) );
if(!return_string) {
return CFSTR("");
}
return return_string;
}
static bool
upcomingToday(CFDictionaryRef event, int today_cf)
{
static const int kAllowScheduleWindowSeconds = 5;
CFGregorianDate greg_now;
CFTimeZoneRef tizzy;
uint32_t secondsToday;
int secondsScheduled;
int days_mask;
if(!event) return false;
days_mask = getRepeatingDictionaryDayMask(event);
if(!(days_mask & (1 << (today_cf-1)))) return false;
tizzy = CFTimeZoneCopySystem();
greg_now = CFAbsoluteTimeGetGregorianDate(
CFAbsoluteTimeGetCurrent(), tizzy);
CFRelease(tizzy);
secondsToday = 60 * ((greg_now.hour*60) + greg_now.minute);
secondsScheduled = 60 * getRepeatingDictionaryMinutes(event);
if(secondsScheduled >= (secondsToday + kAllowScheduleWindowSeconds))
return true;
else
return false;
}
static int
daysUntil(CFDictionaryRef event, int today_cf_day_of_week)
{
int days_mask = getRepeatingDictionaryDayMask(event);
int check = today_cf_day_of_week % 7;
if(0 == days_mask) return -1;
if(upcomingToday(event, today_cf_day_of_week)) return 0;
while(!(days_mask & (1<<check)))
{
check = (check + 1) % 7;
}
check -= today_cf_day_of_week;
check++;
check += 7;
check %= 7;
if(check == 0) check = 7;
return check;
}
static bool
_eventAlreadyScheduled(CFDateRef ev_date, CFStringRef app_name, CFStringRef event_type)
{
CFArrayRef all_events = NULL;
CFDictionaryRef this_event = NULL;
int i;
int count;
bool ret = false;
CFStringRef keys[3];
CFTypeRef vals[3];
CFDictionaryRef new_event = NULL;
keys[0] = CFSTR(kIOPMPowerEventTimeKey);
vals[0] = ev_date;
keys[1] = CFSTR(kIOPMPowerEventAppNameKey);
vals[1] = app_name; keys[2] = CFSTR(kIOPMPowerEventTypeKey);
vals[2] = event_type; new_event = CFDictionaryCreate(0, (const void **)keys, (const void **)vals, 3,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if(!new_event) goto exit;
all_events = IOPMCopyScheduledPowerEvents();
if(!all_events) goto exit;
count = CFArrayGetCount(all_events);
for(i=0; i<count; i++)
{
this_event = CFArrayGetValueAtIndex(all_events, i);
if(CFEqual(this_event, new_event)) ret = true;
}
exit:
if(new_event) CFRelease(new_event);
if(all_events) CFRelease(all_events);
return ret;
}
static void
scheduleNextRepeatingEvent(CFDictionaryRef event)
{
CFGregorianDate greg;
CFTimeZoneRef tizzy;
CFAbsoluteTime ev_time;
CFDateRef ev_date;
int days;
int minutes_scheduled;
int cf_day_of_week;
IOReturn ret;
if(!event) return;
tizzy = CFTimeZoneCopySystem();
cf_day_of_week = CFAbsoluteTimeGetDayOfWeek(
CFAbsoluteTimeGetCurrent(), tizzy);
days = daysUntil(event, cf_day_of_week);
greg = CFAbsoluteTimeGetGregorianDate(
CFAbsoluteTimeGetCurrent() + days*(60*60*24),
tizzy);
minutes_scheduled = getRepeatingDictionaryMinutes(event);
greg.hour = minutes_scheduled/60;
greg.minute = minutes_scheduled%60;
greg.second = 0.0;
ev_time = CFGregorianDateGetAbsoluteTime(greg, tizzy);
ev_date = CFDateCreate(kCFAllocatorDefault, ev_time);
if( !_eventAlreadyScheduled(ev_date, CFSTR("Repeating"),
getRepeatingDictionaryType(event)) )
{
ret = IOPMSchedulePowerEvent(ev_date, CFSTR("Repeating"),
getRepeatingDictionaryType(event));
}
CFRelease(ev_date);
CFRelease(tizzy);
}
__private_extern__ void
RepeatingAutoWakeRepeatingEventOcurred(CFDictionaryRef event)
{
CFStringRef type = getRepeatingDictionaryType(event);
if(!type) return;
if( CFEqual(type, CFSTR(kIOPMAutoSleep))
|| CFEqual(type, CFSTR(kIOPMAutoShutdown)) )
{
scheduleNextRepeatingEvent(repeatingPowerOff);
} else {
scheduleNextRepeatingEvent(repeatingPowerOn);
}
return;
}
__private_extern__ void
RepeatingAutoWakePrefsHaveChanged(void)
{
newRepeatingEvents = IOPMCopyRepeatingPowerEvents();
if(newRepeatingEvents && currentRepeatingEvents &&
CFEqual(newRepeatingEvents, currentRepeatingEvents))
{
CFRelease(newRepeatingEvents);
return;
} else {
if(!newRepeatingEvents && !currentRepeatingEvents) return;
}
if(currentRepeatingEvents)
{
CFRelease(currentRepeatingEvents);
currentRepeatingEvents = 0;
}
currentRepeatingEvents = newRepeatingEvents;
cancelAllRepeatingEvents();
repeatingPowerOff = NULL;
repeatingPowerOn = NULL;
if(!currentRepeatingEvents) return;
repeatingPowerOff = isA_CFDictionary(CFDictionaryGetValue(
currentRepeatingEvents,
CFSTR(kIOPMRepeatingPowerOffKey)));
repeatingPowerOn = isA_CFDictionary(CFDictionaryGetValue(
currentRepeatingEvents,
CFSTR(kIOPMRepeatingPowerOnKey)));
if( !is_valid_repeating_dictionary(repeatingPowerOff)
|| !is_valid_repeating_dictionary(repeatingPowerOn) )
{
syslog(LOG_INFO, "PMCFGD: Invalid formatted repeating power event dictionary\n");
return;
}
scheduleNextRepeatingEvent(repeatingPowerOn);
scheduleNextRepeatingEvent(repeatingPowerOff);
}
__private_extern__ void
RepeatingAutoWakeSleepWakeNotification(natural_t messageType, int runState)
{
if ((kIOMessageSystemHasPoweredOn == messageType)
&& (kRStateNormal == runState))
{
scheduleNextRepeatingEvent(repeatingPowerOn);
scheduleNextRepeatingEvent(repeatingPowerOff);
}
}
__private_extern__ void
RepeatingAutoWake_prime(void)
{
RepeatingAutoWakePrefsHaveChanged();
}