/* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.1 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the * License for the specific language governing rights and limitations * under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* * Copyright (c) 2003 Apple Computer, Inc. All rights reserved. * * HISTORY * * 30-Jan-03 ebold created * */ #include #include #include #include #include #include #include #include #include #include #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 }; /* AutoWakeScheduler overview * * PURPOSE * Set wake and power on timers to automatically power on the machine at a * user/app requested time. * Requests come via IOKit/pwr_mgt/IOPMLib.h:IOPMSchedulePowerEvent() * * On pre-2004 machines this is the PMU's responsibility. * The SMU has taken over the AutoWake/AutoPower role on some post-2004 machines. * The IOPMrootDomain kernel entity routes all requests to the appropriate * controller. * * POWER ON * Every time we set a power on time in the PMU, start a CFTimer to fire at the same time. * (in scheduleShutdownTime()) * If the CFTimer fires, then the machine was not powered off and we should find the * next power on date and schedule that. (in handleTimerPowerOnReset()) * If the machine is powered off, the CFTimer won't fire and the PMU will power the machine. * * WAKE * Wake is simpler than power on, since we get a notification on the way to sleep. * At going-to-sleep time we scan the wake_arr CFArray for the next upcoming * wakeup time (in AutoWakeSleepWakeNotification()) * * LOADING NEW AUTOWAKEUP TIMES * Via SCPreferences notifications * * PURGING OLD TIMES * Done only at boot and wakeup times. We have to write the file back to disk (if it changed). * So to minimize disk access we only purge when we think the disk is "up" anyway. */ // isEntryValidAndFuturistic // Returns true if the CFDictionary is validly formed // AND if the date is in the future // Returns false if anything about the dictionary is invalid // OR if the CFDate is prior to the current time static bool isEntryValidAndFuturistic(CFDictionaryRef wakeup_dict, CFDateRef date_now) { CFDateRef wakeup_date; bool ret = true; wakeup_dict = isA_CFDictionary(wakeup_dict); if(!wakeup_dict) { // bogus entry! ret = false; } else { // valid entry wakeup_date = isA_CFDate(CFDictionaryGetValue(wakeup_dict, CFSTR(kIOPMPowerEventTimeKey))); if(!wakeup_date || (kCFCompareLessThan == CFDateCompare(wakeup_date, date_now, 0))) { // date is too early ret = false; } // otherwise date is after now, and ret = true } return ret; } // Purge past wakeup times // Does not care whether its operating on wakeup or poweron array. // Just purges all entries with a time < now // returns true on success, false on any failure 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; // Note: the value of CFArrayGetcount(arr) can change (decrease) // over iterations of this loop. Don't try to optimize that part out // of the while() i = 0; date_now = CFDateCreate(0, CFAbsoluteTimeGetCurrent()); do { if(!isEntryValidAndFuturistic(CFArrayGetValueAtIndex(arr, i), date_now)) { // Remove entry from the array - its time has past CFArrayRemoveValueAtIndex(arr, i); array_has_changed = true; // do not increment i - higher array entries will be shifted down into its spot } else { // valid entry with wakeup date in the future // assuming the array is in sorted order, the rest of the entries // should also be in the future. break; } } while(i < CFArrayGetCount(arr)); CFRelease(date_now); if(array_has_changed) { // write new, purged array to disk // We should soon-after get a prefs notification to re-read the values 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); } // Find earliest upcoming wakeup time 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