+//*********************************************************************************
+// sysPowerDownHandler
+//
+// Receives a notification when the RootDomain changes state.
+//
+// Allows us to take action on system sleep, power down, and restart after
+// applications have received their power change notifications and replied,
+// but before drivers have powered down. We perform a vfs sync on power down.
+//*********************************************************************************
+
+IOReturn IOPMrootDomain::sysPowerDownHandler( void * target, void * refCon,
+ UInt32 messageType, IOService * service,
+ void * messageArgument, vm_size_t argSize )
+{
+ IOReturn ret;
+ IOPowerStateChangeNotification * params = (IOPowerStateChangeNotification *) messageArgument;
+ IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, service);
+
+ if(!rootDomain)
+ return kIOReturnUnsupported;
+
+ switch (messageType) {
+ case kIOMessageSystemWillSleep:
+ rootDomain->powerOverrideOnPriv(); // start ignoring children's requests
+ // (fall through to other cases)
+
+ // Interested applications have been notified of an impending power
+ // change and have acked (when applicable).
+ // This is our chance to save whatever state we can before powering
+ // down.
+ // We call sync_internal defined in xnu/bsd/vfs/vfs_syscalls.c,
+ // via callout
+
+ // We will ack within 20 seconds
+ params->returnValue = 20 * 1000 * 1000;
+
+ if ( ! OSCompareAndSwap( 0, 1, &gSleepOrShutdownPending ) )
+ {
+ // Purposely delay the ack and hope that shutdown occurs quickly.
+ // Another option is not to schedule the thread and wait for
+ // ack timeout...
+ AbsoluteTime deadline;
+ clock_interval_to_deadline( 30, kSecondScale, &deadline );
+ thread_call_enter1_delayed( rootDomain->diskSyncCalloutEntry,
+ (thread_call_param_t)params->powerRef,
+ deadline );
+ }
+ else
+ thread_call_enter1(rootDomain->diskSyncCalloutEntry, (thread_call_param_t)params->powerRef);
+ ret = kIOReturnSuccess;
+ break;
+
+ case kIOMessageSystemWillPowerOff:
+ case kIOMessageSystemWillRestart:
+ ret = kIOReturnUnsupported;
+ break;
+
+ default:
+ ret = kIOReturnUnsupported;
+ break;
+ }
+ return ret;
+}
+
+//*********************************************************************************
+// displayWranglerNotification
+//
+// Receives a notification when the IODisplayWrangler changes state.
+//
+// Allows us to take action on display dim/undim.
+//
+// When the display goes dim we:
+// - Start the idle sleep timer
+// - set the quick spin down timeout
+//
+// On wake from display dim:
+// - Cancel the idle sleep timer
+// - restore the user's chosen spindown timer from the "quick" spin down value
+//*********************************************************************************
+
+IOReturn IOPMrootDomain::displayWranglerNotification( void * target, void * refCon,
+ UInt32 messageType, IOService * service,
+ void * messageArgument, vm_size_t argSize )
+{
+ IOPMrootDomain * rootDomain = OSDynamicCast(IOPMrootDomain, (IOService *)target);
+ AbsoluteTime deadline;
+ static bool deviceAlreadyPoweredOff = false;
+
+ if(!rootDomain)
+ return kIOReturnUnsupported;
+
+ switch (messageType) {
+ case kIOMessageDeviceWillPowerOff:
+ // The IODisplayWrangler has powered off either because of idle display sleep
+ // or force system sleep.
+
+ // The display wrangler will send the DeviceWillPowerOff message 4 times until
+ // it gets into its lowest state. We only want to act on the first of those 4.
+ if( deviceAlreadyPoweredOff ) return kIOReturnUnsupported;
+
+ deviceAlreadyPoweredOff = true;
+
+ if( rootDomain->extraSleepDelay ) {
+
+ // start the extra sleep timer
+ clock_interval_to_deadline(rootDomain->extraSleepDelay*60, kSecondScale, &deadline );
+ thread_call_enter_delayed(rootDomain->extraSleepTimer, deadline);
+ rootDomain->idleSleepPending = true;
+
+ } else {
+
+ // accelerate disk spin down if spin down timer is non-zero (zero = never spin down)
+ // and if system sleep is non-Never
+ if( (0 != rootDomain->user_spindown) && (0 != rootDomain->sleepSlider) )
+ rootDomain->setQuickSpinDownTimeout();
+ }
+
+ break;
+
+ case kIOMessageDeviceHasPoweredOn:
+
+ // The display has powered on either because of UI activity or wake from sleep/doze
+ deviceAlreadyPoweredOff = false;
+ rootDomain->adjustPowerState();
+
+
+ // cancel any pending idle sleep
+ if(rootDomain->idleSleepPending) {
+ thread_call_cancel(rootDomain->extraSleepTimer);
+ rootDomain->idleSleepPending = false;
+ }
+
+ // Change the spindown value back to the user's selection from our accelerated setting
+ if(0 != rootDomain->user_spindown)
+ rootDomain->restoreUserSpinDownTimeout();
+
+ // Put on the policy maker's on clamp.
+
+ break;
+
+ default:
+ break;
+ }
+ return kIOReturnUnsupported;
+ }
+
+//*********************************************************************************
+// displayWranglerPublished
+//
+// Receives a notification when the IODisplayWrangler is published.
+// When it's published we install a power state change handler.
+//
+//*********************************************************************************
+
+bool IOPMrootDomain::displayWranglerPublished( void * target, void * refCon,
+ IOService * newService)