Mac 电源管理

1 篇文章 0 订阅
这篇博客详细介绍了Mac OS中电源管理配置文件`pmconfigd.c`的内容,包括系统事件处理、电池状态监控、睡眠唤醒回调函数以及与kernel PM的交互。它涵盖了从启动到运行时的各种电源管理任务,如跟踪电池信息、响应登录窗口的关机和重启通知,以及处理时区变化和系统负载。此外,还涉及到了与`ioupmsd`进程的通信和维护系统睡眠状态的相关逻辑。
摘要由CSDN通过智能技术生成

Mac开源代码:pmconfigd.c

/*
 * Copyright (c) 2007 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this
 * file.
 * 
 * The 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, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
 
#include <CoreFoundation/CoreFoundation.h>

#include <syslog.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>
#include <mach/mach.h>
#include <servers/bootstrap.h>
#include <notify.h> 
#include <asl.h>
#include <uuid/uuid.h>
#include <pthread.h>
#include <bsm/libbsm.h>
#include <sys/sysctl.h>
#include <xpc/private.h>
#if !TARGET_OS_EMBEDDED
#include <IOKit/platform/IOPlatformSupportPrivate.h>
#include <AssertMacros.h>
#endif
#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
#include <IOKit/hid/IOHIDKeys.h>

#include <Security/SecTask.h>

#include "powermanagementServer.h" // mig generated

#include "PMStore.h"
#include "PMSettings.h"
#include "UPSLowPower.h"
#include "BatteryTimeRemaining.h"
#include "AutoWakeScheduler.h"
#include "RepeatingAutoWake.h"
#include "PMAssertions.h"
#include "PrivateLib.h"
#include "TTYKeepAwake.h"
#include "PMSystemEvents.h"
#include "SystemLoad.h"
#include "PMConnection.h"
#include "ExternalMedia.h"
#include "Platform.h"

// To support importance donation across IPCs
#include <libproc_internal.h>

#define kIOPMAppName        "Power Management configd plugin"
#define kIOPMPrefsPath      "com.apple.PowerManagement.xml"
#define pwrLogDirName       "/System/Library/PowerEvents"

#ifndef kIOUPSDeviceKey
// Also defined in ioupsd/IOUPSPrivate.h
#define kIOUPSDeviceKey             "UPSDevice"
#define kIOPowerDeviceUsageKey      0x84
#define kIOBatterySystemUsageKey    0x85
#endif

/*
 * BSD notifications from loginwindow indicating shutdown
 */
// kLWShutdownInitiated
//   User clicked shutdown: may be aborted later
#define kLWShutdowntInitiated    "com.apple.system.loginwindow.shutdownInitiated"

// kLWRestartInitiated
//   User clicked restart: may be aborted later
#define kLWRestartInitiated     "com.apple.system.loginwindow.restartinitiated"

// kLWLogoutCancelled
//   A previously initiated shutdown, restart, or logout, has been cancelled.
#define kLWLogoutCancelled      "com.apple.system.loginwindow.logoutcancelled"

// kLWLogoutPointOfNoReturn
//   A previously initiated shutdown, restart, or logout has succeeded, and is 
//   no longer abortable by anyone. Point of no return!
#define kLWLogoutPointOfNoReturn    "com.apple.system.loginwindow.logoutNoReturn"

// kLWSULogoutInitiated
//   Loginwindow is beginning a sequence of 1. logout, 2. software update, 3. then restart.
#define kLWSULogoutInitiated     "com.apple.system.loginwindow.sulogoutinitiated"

#define kDWTMsgHandlerDelay         10  // Time(in secs) for which DW Thermal msg handler is delayed

#define LogObjectRetainCount(x, y) do {} while(0)
/* #define LogObjectRetainCount(x, y) do { \
    asl_log(NULL, NULL, ASL_LEVEL_ERR, "%s: kernel retain = %d, user retain = %d\n", \
        x, IOObjectGetKernelRetainCount(y), IOObjectGetUserRetainCount(y)); } while(0)
*/

// Global keys
static CFStringRef              gTZNotificationNameString           = NULL;

static SCPreferencesRef         gESPreferences                      = NULL;

static io_connect_t             _pm_ack_port                        = 0;
static io_iterator_t            _ups_added_noteref                  = 0;
static int                      _alreadyRunningIOUPSD               = 0;

static int                      gCPUPowerNotificationToken          = 0;
static bool                     gExpectingWakeFromSleepClockResync  = false;
static CFAbsoluteTime           *gLastWakeTime                      = NULL;
static CFTimeInterval           *gLastSMCS3S0WakeInterval           = NULL;
static CFStringRef              gCachedNextSleepWakeUUIDString      = NULL;
#if !TARGET_OS_EMBEDDED
static int                      gLastWakeTimeToken                  = -1;
static int                      gLastSMCS3S0WakeIntervalToken       = -1;
#endif

typedef struct {
    int  shutdown;
    int  restart;
    int  cancel;
    int  pointofnoreturn;
    int  su;
} LoginWindowNotifyTokens;
static LoginWindowNotifyTokens  lwNotify = {0,0,0,0,0};

static CFStringRef              gConsoleNotifyKey                   = NULL;
static bool                     gDisplayIsAsleep = false;
static CFAbsoluteTime           gSleepFromUserWakeTime = 0;
static struct timeval           gLastSleepTime                      = {0, 0};

static mach_port_t              serverPort                          = MACH_PORT_NULL;
__private_extern__ CFMachPortRef pmServerMachPort                   = NULL;
#if !TARGET_OS_EMBEDDED
static bool                     gSMCSupportsWakeupTimer             = true;
static int                      _darkWakeThermalEventCount          = 0;
static dispatch_source_t        gDWTMsgDispatch; /* Darkwake thermal emergency message handler dispatch */
#endif

#if TCPKEEPALIVE
extern TCPKeepAliveStruct           *gTCPKeepAlive;
#endif

// defined by MiG
extern boolean_t powermanagement_server(mach_msg_header_t *, mach_msg_header_t *);


// foward declarations
static void initializeESPrefsDynamicStore(void);
static void initializeInterestNotifications(void);
static void initializeHIDInterestNotifications(
                int usagePage,
                IONotificationPortRef notify_port);
static void initializeTimezoneChangeNotifications(void);
static void initializeCalendarResyncNotification(void);
static void initializeShutdownNotifications(void);
static void initializeRootDomainInterestNotifications(void);
#if !TARGET_OS_EMBEDDED
static void initializeUserNotifications(void);
static void enableSleepWakeWdog();
#endif
static void initializeSleepWakeNotifications(void);

static void SleepWakeCallback(void *,io_service_t, natural_t, void *);
static void ESPrefsHaveChanged(
                SCPreferencesRef prefs,
                SCPreferencesNotification notificationType,
                void *info);
static void _ioupsd_exited(pid_t, int, struct rusage *, void *);
static void UPSDeviceAdded(void *, io_iterator_t);
static void ioregBatteryMatch(void *, io_iterator_t);
static void ioregBatteryInterest(void *, io_service_t, natural_t, void *);
static void RootDomainInterest(void *, io_service_t, natural_t, void *);
static void broadcastGMTOffset(void);

static void pushNewSleepWakeUUID(void);

static void calendarRTCDidResync(
                CFMachPortRef port, 
                void *msg,
                CFIndex size,
                void *info);

static void lwShutdownCallback( 
                CFMachPortRef port,
                void *msg,
                CFIndex size,
                void *info);

static void timeZoneChangedCallBack(
                CFNotificationCenterRef center, 
                void *observer, 
                CFStringRef notificationName, 
                const void *object, 
                CFDictionaryRef userInfo);

static void displayMatched(void *, io_iterator_t);
static void displayPowerStateChange(
                void *ref, 
                io_service_t service, 
                natural_t messageType, 
                void *arg);

static boolean_t pm_mig_demux(
                mach_msg_header_t * request,
                mach_msg_header_t * reply);

static void mig_server_callback(
                CFMachPortRef port, 
                void *msg, 
                CFIndex size, 
                void *info);

static void incoming_XPC_connection(xpc_connection_t);
static void xpc_register(void);

static void AppClaimWakeReason(xpc_object_t claim);




kern_return_t _io_pm_set_active_profile(
                mach_port_t         server,
                audit_token_t       token,
                vm_offset_t         profiles_ptr,
                mach_msg_type_number_t    profiles_len,
                int                 *result);

kern_return_t _io_pm_last_wake_time(
                mach_port_t             server,
                vm_offset_t             *out_wake_data,
                mach_msg_type_number_t  *out_wake_len,
                vm_offset_t             *out_delta_data,
                mach_msg_type_number_t  *out_delta_len,
                int                     *return_val);



static CFStringRef              
serverMPCopyDescription(const void *info)
{
    return CFStringCreateWithFormat(NULL, NULL, CFSTR("<IOKit Power Management MIG server>"));
}

// Callback is registered in PrivateLib.c
__private_extern__ void dynamicStoreNotifyCallBack(
                SCDynamicStoreRef   store,
                CFArrayRef          changedKeys,
                void                *info);


/* load
 *
 * configd entry point
 */




int main(int argc __unused, char *argv[] __unused)
{
    CFRunLoopSourceRef      cfmp_rls = 0;
    CFMachPortContext       context  = { 0, (void *)1, NULL, NULL, serverMPCopyDescription };
    kern_return_t           kern_result = 0;
    
    xpc_register();
    
    kern_result = bootstrap_check_in(
                            bootstrap_port, 
                            kIOPMServerBootstrapName,
                            &serverPort);

#if TARGET_OS_EMBEDDED
    if (BOOTSTRAP_SUCCESS != kern_result) {
        kern_result = mach_port_allocate(
                                mach_task_self(), 
                                MACH_PORT_RIGHT_RECEIVE, 
                                &serverPort);

        if (KERN_SUCCESS == kern_result) {
            kern_result = mach_port_insert_right(
                                mach_task_self(), 
                                serverPort, serverPort, 
                                MACH_MSG_TYPE_MAKE_SEND);
        }
    
        if (KERN_SUCCESS == kern_result) {
            kern_result = bootstrap_register(
                                bootstrap_port, 
                                kIOPMServerBootstrapName, 
                                serverPort);
        }
    }
#endif

    if (BOOTSTRAP_SUCCESS != kern_result) {
        syslog(LOG_ERR, "PM configd: bootstrap_register \"%s\" error = %d\n",
                                kIOPMServerBootstrapName, kern_result);
    }

    if (MACH_PORT_NULL != serverPort)
    {
        // Finish setting up mig handler callback on pmServerMachPort
        pmServerMachPort = _SC_CFMachPortCreateWithPort(
                                "PowerManagement",
                                serverPort, 
                                mig_server_callback, 
                                &context);
    }
    if (pmServerMachPort) {
        cfmp_rls = CFMachPortCreateRunLoopSource(0, pmServerMachPort, 0);
        if (cfmp_rls) {
            CFRunLoopAddSource(CFRunLoopGetCurrent(), cfmp_rls, kCFRunLoopDefaultMode);
            CFRelease(cfmp_rls);
        }
    }

    _getPMRunLoop();
    
    PMStoreLoad();
    
    initializeESPrefsDynamicStore();
    initializeInterestNotifications();
    initializeTimezoneChangeNotifications();
    initializeCalendarResyncNotification();    
    initializeShutdownNotifications();
    initializeRootDomainInterestNotifications();
    
#if !TARGET_OS_EMBEDDED
    initializeUserNotifications();
    _oneOffHacksSetup();
#endif
    
    initializeSleepWakeNotifications();

    // Prime the messagetracer UUID pump
    pushNewSleepWakeUUID();
    
    BatteryTimeRemaining_prime();
    PMSettings_prime();
    AutoWake_prime();
    PMAssertions_prime();
    PMSystemEvents_prime();
    SystemLoad_prime();
    PMConnection_prime();

#if !TARGET_OS_EMBEDDED
    UPSLowPower_prime();
    TTYKeepAwake_prime();
    ExternalMedia_prime();

    createOnBootAssertions();
    enableSleepWakeWdog();
#endif

    _unclamp_silent_running(false);
    notify_post(kIOUserAssertionReSync);
    logASLMessagePMStart();

    CFRunLoopRun();
    return 0;
}




static void ioregBatteryMatch(
    void *refcon, 
    io_iterator_t b_iter) 
{
    IOPMBattery                 *tracking;
    IONotificationPortRef       notify = (IONotificationPortRef)refcon;
    io_registry_entry_t         battery;
    io_object_t                 notification_ref;
    
    while((battery = (io_registry_entry_t)IOIteratorNext(b_iter))) 
    {
        // Add battery to our list of batteries
        tracking = _newBatteryFound(battery);
        
        LogObjectRetainCount("PM::BatteryMatch(M0) me", battery);
        
        // And install an interest notification on it
        IOServiceAddInterestNotification(notify, battery, 
                            kIOGeneralInterest, ioregBatteryInterest,
                            (void *)tracking, &notification_ref);

        LogObjectRetainCount("PM::BatteryMatch(M1) me", battery);
        LogObjectRetainCount("PM::BatteryMatch(M1) msg_port", notification_ref);

        tracking->msg_port = notification_ref;
        IOObjectRelease(battery);
    }
    InternalEvaluateAssertions();
    InternalEvalConnections();
}


static void ioregBatteryInterest(
    void *refcon, 
    io_service_t batt, 
    natural_t messageType, 
    void *messageArgument)
{
    IOPMBattery         *changed_batt = (IOPMBattery *)refcon;
    IOPMBattery         **batt_stats;

    if(kIOPMMessageBatteryStatusHasChanged == messageType)
    {
        // Update the arbiter
        changed_batt->me = (io_registry_entry_t)batt;
        _batteryChanged(changed_batt);

        LogObjectRetainCount("PM:BatteryInterest(B0) msg_port", changed_batt->msg_port);
        LogObjectRetainCount("PM:BatteryInterest(B1) msg_port", changed_batt->me);

        batt_stats = _batteries();
        kernelPowerSourcesDidChange(changed_batt);
        SystemLoadBatteriesHaveChanged(batt_stats);
        InternalEvaluateAssertions();
        InternalEvalConnections();
    }

    return;
}


__private_extern__ void 
ClockSleepWakeNotification(IOPMSystemPowerStateCapabilities old_cap,
                           IOPMSystemPowerStateCapabilities new_cap,
                           uint32_t changeFlags)
{
    // Act on the notification before the dark wake to sleep transition
    if (CAPABILITY_BIT_CHANGED(new_cap, old_cap, kIOPMSystemPowerStateCapabilityCPU) &&
        BIT_IS_SET(old_cap, kIOPMSystemPowerStateCapabilityCPU) &&
        BIT_IS_SET(changeFlags, kIOPMSystemCapabilityWillChange))
    {
#if !TARGET_OS_EMBEDDED
        // write SMC Key to re-enable SMC timer
        _smcWakeTimerPrimer();
#endif

        // Stash the last sleep time to ignore clock changes before
        // this sleep is completed.
        size_t len = sizeof(gLastSleepTime);
        if (sysctlbyname("kern.sleeptime", &gLastSleepTime, &len, NULL, 0)) {
            gLastSleepTime.tv_sec = 0;
            gLastSleepTime.tv_usec = 0;
        }

        // The next clock resync occuring on wake from sleep shall be marked
        // as the wake time.
        gExpectingWakeFromSleepClockResync = true;

#if !TARGET_OS_EMBEDDED
        if (gLastWakeTimeToken >= 0)
            notify_set_state(gLastWakeTimeToken, 0);
        if (gLastSMCS3S0WakeIntervalToken >= 0)
            notify_set_state(gLastSMCS3S0WakeIntervalToken, 0);
#endif
        // tell clients what our timezone offset is
        broadcastGMTOffset();
    }
}


/*  
 * 
 * Receives notifications on system sleep and system wake.
 * This callback is not called for maintenance sleep/wake.
 */
static void
SleepWakeCallback(
    void            *port,
    io_service_t    rootdomainservice,
    natural_t       messageType,
    void            *acknowledgementToken)
{

    BatteryTimeRemainingSleepWakeNotification(messageType);
    PMSettingsSleepWakeNotification(messageType);

    // Log Message to MessageTracer

    // Acknowledge message
    switch ( messageType ) {
        case kIOMessageSystemWillSleep:
            if (isUserActiveRootDomain) {
                gSleepFromUserWakeTime = CFAbsoluteTimeGetCurrent();
                userActiveHandleSleep();
            }
            // Fall thru
        case kIOMessageCanSystemSleep:
            IOAllowPowerChange(_pm_ack_port, (long)acknowledgementToken);
            break;

        case kIOMessageSystemHasPoweredOn:
        case kIOMessageSystemWillNotSleep:
            _set_sleep_revert(true);
            break;

        default:
            break;
    }
}

/* ESPrefsHaveChanged
 *
 * Is the handler that configd calls when someone "applies" new Energy Saver
 * Preferences. Since the preferences have probably changed, we re-read them
 * from disk and transmit the new settings to the kernel.
 */
static void 
ESPrefsHaveChanged(
    SCPreferencesRef prefs,
    SCPreferencesNotification notificationType,
    void *info)
{
    if ((kSCPreferencesNotificationCommit & notificationType) == 0)
        return;

    if (gESPreferences == prefs)
    {
        // Tell ES Prefs listeners that the prefs have changed
        PMSettingsPrefsHaveChanged();
        mt2EvaluateSystemSupport();
#if !TARGET_OS_EMBEDDED
        UPSLowPowerPrefsHaveChanged();
        TTYKeepAwakePrefsHaveChanged();
#endif
        SystemLoadPrefsHaveChanged();
    }

    return;
}


/* _ioupsd_exited
 *
 * Gets called (by configd) when /usr/libexec/ioupsd exits
 */
static void _ioupsd_exited(
    pid_t           pid,
    int             status,
    struct rusage   *rusage,
    void            *context)
{
    if(0 != status)
    {
        // ioupsd didn't exit cleanly.
        syslog(
            LOG_ERR, 
           "PowerManagement: /usr/libexec/ioupsd(%d) has exited with status %d\n", 
           pid, 
           status);
        
        // relaunch
        char *argv[2] = {"/usr/libexec/ioupsd", NULL};
        _SCDPluginExecCommand(&_ioupsd_exited, 0, 0, 0,
                              "/usr/libexec/ioupsd", argv);
    } else {
        _alreadyRunningIOUPSD = 0;
    }
}


/* UPSDeviceAdded
 *
 * A UPS has been detected running on the system.
 * 
 */
static void UPSDeviceAdded(void *refCon, io_iterator_t iterator)
{
    io_object_t                 upsDevice = MACH_PORT_NULL;
        
    while ( (upsDevice = IOIteratorNext(iterator)) )
    {
        // If not running, launch the management process ioupsd now.
        if(!_alreadyRunningIOUPSD) {
            char *argv[2] = {"/usr/libexec/ioupsd", NULL};

            _alreadyRunningIOUPSD = 1;
            _SCDPluginExecCommand(&_ioupsd_exited, 0, 0, 0,
                "/usr/libexec/ioupsd", argv);
        }
        IOObjectRelease(upsDevice);
    }
}


/* timeZoneChangedCallback
 *
 * When our timezone offset changes, tell interested drivers.
 */
static void 
timeZoneChangedCallBack(
    CFNotificationCenterRef center, 
    void *observer, 
    CFStringRef notificationName, 
    const void *object, 
    CFDictionaryRef userInfo)
{
    if( CFEqual(notificationName, gTZNotificationNameString) )
    {
        broadcastGMTOffset();
    }
}


/* broadcastGMTOffset
 *
 * Tell the timezone clients what the seconds offset from GMT is. This info
 * is delivered via the kernel PMSettings interface.
 *
 * Notifications are sent:
 *    - at boot time
 *    - when timezone changes
 *    - at sleep time*
 *    - at display sleep time*
 *
 *    * PM configd does not receive a notification when daylight savings time
 *      changes. In case the system has entered daylight savings time since
 *      boot, we re-broadcast the tz offset at sleep and display sleep.
 */
static void 
broadcastGMTOffset(void)
{
    CFTimeZoneRef               tzr = NULL;
    CFNumberRef                 n = NULL;
    int                         secondsOffset = 0;

    CFTimeZoneResetSystem();
    tzr = CFTimeZoneCopySystem();
    if(!tzr) return;

    secondsOffset = (int)CFTimeZoneGetSecondsFromGMT(tzr, CFAbsoluteTimeGetCurrent());
    n = CFNumberCreate(0, kCFNumberIntType, &secondsOffset);
    if(!n) {
        goto exit;
    }
    
    // Tell the root domain what our timezone's offset from GMT is.
    // IOPMrootdomain will relay the message on to interested PMSetting clients.
    _setRootDomainProperty(CFSTR("TimeZoneOffsetSeconds"), n);

exit:
    if(tzr) CFRelease(tzr);
    if(n) CFRelease(n);
    return;
}

/* lwShutdownCallback
 *
 *
 *
 * loginwindow shutdown handler
 *
 */ 
static void lwShutdownCallback( 
    CFMachPortRef port,
    void *msg,
    CFIndex size,
    void *info)
{
    mach_msg_header_t   *header = (mach_msg_header_t *)msg;
    CFNumberRef n = NULL;
    static bool amidst_shutdown = false;
    static int consoleShutdownState = kIOPMStateConsoleShutdownNone;
    static int lastConsoleShutdownState = 0;

    if (header->msgh_id == gCPUPowerNotificationToken)
    {
        // System CPU power status has changed
        SystemLoadCPUPowerHasChanged(NULL);
    } else if (header->msgh_id == lwNotify.su)
    {
        // Loginwindow is logging out to begin a several-minute
        // software update. We'll suppress the immediately next shoutdown
        // and logout messages.
        consoleShutdownState = kIOPMStateConsoleSULogoutInitiated;

    } else if (header->msgh_id == lwNotify.shutdown)
    {
        // Loginwindow put a shutdown confirm panel up on screen
        // The user has not necessarily even clicked on it yet
        amidst_shutdown = true;        
        consoleShutdownState = kIOPMStateConsoleShutdownPossible;

    } else if (header->msgh_id == lwNotify.restart)
    {
        // Loginwindow put a restart confirm panel up on screen
        // The user has not necessarily even clicked on it yet
        amidst_shutdown = true;
        consoleShutdownState = kIOPMStateConsoleShutdownPossible;

    } else if (header->msgh_id == lwNotify.cancel)
    {
        amidst_shutdown = false;
        consoleShutdownState = kIOPMStateConsoleShutdownNone;

    } else if (amidst_shutdown 
            && (header->msgh_id == lwNotify.pointofnoreturn))
    {
        // Whatever shutdown or restart that was in progress has succeeded.
        // All apps are quit, there's no more user input required. We will
        // hereby disable sleep for the remainder of time spent shutting down
        // this machine.

        _setRootDomainProperty(CFSTR("System Shutdown"), kCFBooleanTrue);
        consoleShutdownState = kIOPMStateConsoleShutdownCertain;
    }
    
    // Tell interested kernel drivers where we are in the GUI shutdown.
    if (lastConsoleShutdownState != consoleShutdownState) {
    
        n = CFNumberCreate(0, kCFNumberIntType, &consoleShutdownState);
        if (n) {
            _setRootDomainProperty( CFSTR(kIOPMStateConsoleShutdown), n) ;
            CFRelease(n);
        }

        lastConsoleShutdownState = consoleShutdownState;
    }
    
    
    return;
}


/* displayPowerStateChange
 *
 * displayPowerStateChange gets notified when the display changes power state.
 * Power state changes look like this:
 * (1) Full power -> dim
 * (2) dim -> display sleep
 * (3) display sleep -> display sleep
 * 
 * We're interested in state transition 2. On transition to state 2 we
 * broadcast the system clock's offset from GMT.
 */
static void 
displayPowerStateChange(void *ref, io_service_t service, natural_t messageType, void *arg)
{
    IOPowerStateChangeNotification *params = (IOPowerStateChangeNotification*) arg;
    bool prevState = gDisplayIsAsleep;

    switch (messageType)
    {
            // Display Wrangler power stateNumber values
            // 4 Display ON
            // 3 Display Dim
            // 2 Display Sleep
            // 1 Not visible to user
            // 0 Not visible to user

        case kIOMessageDeviceWillPowerOff:
            if ( params->stateNumber != 4 )
            {
                gDisplayIsAsleep = true;
            }

            if ( params->stateNumber <= 1)
            {
               // Notify a SystemLoad state change when display is completely off
                SystemLoadDisplayPowerStateHasChanged(gDisplayIsAsleep);
                // Display is transition from dim to full sleep.
                broadcastGMTOffset();            
            }
            break;
            
        case kIOMessageDeviceHasPoweredOn:
            if ( params->stateNumber == 4 )
            {
                gDisplayIsAsleep = false;            
                SystemLoadDisplayPowerStateHasChanged(gDisplayIsAsleep);
            }

            break;
    }
    
    if (prevState != gDisplayIsAsleep) {
        logASLDisplayStateChange();
    }
}

__private_extern__ bool isDisplayAsleep( )
{
    return gDisplayIsAsleep;
}

/* initializeESPrefsDynamicStore
 *
 * Registers a handler that configd calls when someone changes com.apple.PowerManagement.xml
 */
static void
initializeESPrefsDynamicStore(void)
{
    gESPreferences = SCPreferencesCreate(
                    kCFAllocatorDefault,
                    CFSTR("com.apple.configd.powermanagement"),
                    CFSTR(kIOPMPrefsPath));

    if (gESPreferences)
    {
        SCPreferencesSetCallback(
                    gESPreferences,
                    (SCPreferencesCallBack)ESPrefsHaveChanged,
                    (SCPreferencesContext *)NULL);

        SCPreferencesScheduleWithRunLoop(
                    gESPreferences,
                    CFRunLoopGetCurrent(),
                    kCFRunLoopDefaultMode);
    }
    
    return;
}


/* pushNewSleepWakeUUID
 *
 * Called (1) At boot, and (2) When kernel PM uses its current UUID.
 * We pre-allocate the UUID here and send it to the kernel, but the kernel
 * will not activate it until the _next_ sleep/wake session begins.
 *
 * Global gCachedNextSleepWakeUUIDString will always reflect the next
 * upcoming sleep/wake session UUID; not the current UUID.
 */
static void pushNewSleepWakeUUID(void)
{
    uuid_t              new_uuid;
    uuid_string_t       new_uuid_string;

    io_registry_entry_t root_domain = getRootDomain();

    if (IO_OBJECT_NULL == root_domain) {
        return;
    }
    
    uuid_generate(new_uuid);
    uuid_unparse_upper(new_uuid, new_uuid_string);
    
    if (gCachedNextSleepWakeUUIDString) 
    {
        CFRelease(gCachedNextSleepWakeUUIDString);

        gCachedNextSleepWakeUUIDString = NULL;
    }

    if ((gCachedNextSleepWakeUUIDString = CFStringCreateWithCString(0, new_uuid_string, kCFStringEncodingUTF8))) 
    {
        IORegistryEntrySetCFProperty(root_domain, CFSTR(kIOPMSleepWakeUUIDKey), gCachedNextSleepWakeUUIDString);
    }
    return;
}


static void AppClaimWakeReason(xpc_object_t claim)
{
    const char              *id;
    const char              *reason;
    xpc_object_t            d;

    if (!claim) {
        return;
    }

    id = xpc_dictionary_get_string(claim, "identity"),
    reason = xpc_dictionary_get_string(claim, "reason"),
    d = xpc_dictionary_get_value(claim, "description");

    logASLAppWakeReason(id, reason);
}

static void incoming_XPC_connection(xpc_connection_t peer)
{
    xpc_connection_set_event_handler(peer,
             ^(xpc_object_t event) {
                 SecTaskRef secTask = NULL;
                 CFTypeRef  entitled_DarkWakeControl = NULL;
                 audit_token_t token;

                 xpc_connection_get_audit_token(peer, &token);

                 secTask = SecTaskCreateWithAuditToken(kCFAllocatorDefault, token);
                 if (secTask) {
                     entitled_DarkWakeControl = SecTaskCopyValueForEntitlement(secTask, kIOPMDarkWakeControlEntitlement, NULL);
                 }

                 if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {

                     xpc_object_t inEvent;

                     if (entitled_DarkWakeControl
                         && (inEvent = xpc_dictionary_get_value(event, "claimSystemWakeEvent")))
                     {
                         AppClaimWakeReason(inEvent);
                     }
                 }

                 if (secTask) {
                     CFRelease(secTask);
                 }
                 if (entitled_DarkWakeControl) {
                     CFRelease(entitled_DarkWakeControl);
                 }
             });

    xpc_connection_resume(peer);
    return;
}

static void xpc_register(void)
{
    xpc_connection_t        connection;

    connection = xpc_connection_create_mach_service(
                                                    "com.apple.iokit.powerdxpc",
                                                    dispatch_get_main_queue(),
                                                    XPC_CONNECTION_MACH_SERVICE_LISTENER);

    xpc_connection_set_target_queue(connection, dispatch_get_main_queue());

    xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
            xpc_type_t type = xpc_get_type(event);
            if (type == XPC_TYPE_CONNECTION) {
                incoming_XPC_connection((xpc_connection_t)event);
            }
        });
    
    xpc_connection_resume(connection);
}


static boolean_t 
pm_mig_demux(
    mach_msg_header_t * request,
    mach_msg_header_t * reply)
{
    mach_dead_name_notification_t *deadRequest = 
                    (mach_dead_name_notification_t *)request;
    boolean_t processed = FALSE;

    processed = powermanagement_server(request, reply);

    if (processed) 
        return true;
    
    if (MACH_NOTIFY_DEAD_NAME == request->msgh_id) 
    {
        __MACH_PORT_DEBUG(true, "pm_mig_demux: Dead name port should have 1+ send right(s)", deadRequest->not_port);

        PMConnectionHandleDeadName(deadRequest->not_port);

        __MACH_PORT_DEBUG(true, "pm_mig_demux: Deallocating dead name port", deadRequest->not_port);
        mach_port_deallocate(mach_task_self(), deadRequest->not_port);
        
        reply->msgh_bits            = 0;
        reply->msgh_remote_port     = MACH_PORT_NULL;

        return TRUE;
    }

    // mig request is not in our subsystem range!
    // generate error reply packet
    reply->msgh_bits        = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(request->msgh_bits), 0);
    reply->msgh_remote_port = request->msgh_remote_port;
    reply->msgh_size        = sizeof(mig_reply_error_t);    /* Minimal size */
    reply->msgh_local_port  = MACH_PORT_NULL;
    reply->msgh_id          = request->msgh_id + 100;
    ((mig_reply_error_t *)reply)->NDR = NDR_record;
    ((mig_reply_error_t *)reply)->RetCode = MIG_BAD_ID;
    
    return processed;
}



static void
mig_server_callback(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
    mig_reply_error_t * bufRequest = msg;
    mig_reply_error_t * bufReply = CFAllocatorAllocate(
        NULL, _powermanagement_subsystem.maxsize, 0);
    mach_msg_return_t   mr;
    int                 options;

    __MACH_PORT_DEBUG(true, "mig_server_callback", serverPort);
    
    /* we have a request message */
    (void) pm_mig_demux(&bufRequest->Head, &bufReply->Head);

    if (!(bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) &&
         (bufReply->RetCode != KERN_SUCCESS)) {

        if (bufReply->RetCode == MIG_NO_REPLY) {
            /*
             * This return code is a little tricky -- it appears that the
             * demux routine found an error of some sort, but since that
             * error would not normally get returned either to the local
             * user or the remote one, we pretend it's ok.
             */
            goto out;
            
        }

        /*
         * destroy any out-of-line data in the request buffer but don't destroy
         * the reply port right (since we need that to send an error message).
         */
        bufRequest->Head.msgh_remote_port = MACH_PORT_NULL;
        mach_msg_destroy(&bufRequest->Head);
    }

    if (bufReply->Head.msgh_remote_port == MACH_PORT_NULL) {
        /* no reply port, so destroy the reply */
        if (bufReply->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) {
            mach_msg_destroy(&bufReply->Head);
        }
        goto out;
    }

    /*
     * send reply.
     *
     * We don't want to block indefinitely because the client
     * isn't receiving messages from the reply port.
     * If we have a send-once right for the reply port, then
     * this isn't a concern because the send won't block.
     * If we have a send right, we need to use MACH_SEND_TIMEOUT.
     * To avoid falling off the kernel's fast RPC path unnecessarily,
     * we only supply MACH_SEND_TIMEOUT when absolutely necessary.
     */

    options = MACH_SEND_MSG;
    if (MACH_MSGH_BITS_REMOTE(bufReply->Head.msgh_bits) != MACH_MSG_TYPE_MOVE_SEND_ONCE) {
        options |= MACH_SEND_TIMEOUT;
    }
    mr = mach_msg(&bufReply->Head,          /* msg */
              options,                      /* option */
              bufReply->Head.msgh_size,     /* send_size */
              0,                            /* rcv_size */
              MACH_PORT_NULL,               /* rcv_name */
              MACH_MSG_TIMEOUT_NONE,        /* timeout */
              MACH_PORT_NULL);              /* notify */


    /* Has a message error occurred? */
    switch (mr) {
        case MACH_SEND_INVALID_DEST:
        case MACH_SEND_TIMED_OUT:
            /* the reply can't be delivered, so destroy it */
            mach_msg_destroy(&bufReply->Head);
            break;

        default :
            /* Includes success case.  */
            break;
    }


out:
    CFAllocatorDeallocate(NULL, bufReply);
    return;

}

/* dynamicStoreNotifyCallBack
 *
 * Changed Keys in dynamic store
 *
 */
__private_extern__
void dynamicStoreNotifyCallBack(
    SCDynamicStoreRef   store,
    CFArrayRef          changedKeys,
    void                *info)
{
    CFRange range = CFRangeMake(0,
                CFArrayGetCount(changedKeys));

    // Check for Console user change
    if (gConsoleNotifyKey
        && CFArrayContainsValue(changedKeys, 
                                range,
                                gConsoleNotifyKey))
    {
#if !TARGET_OS_EMBEDDED
        SystemLoadUserStateHasChanged();
#endif
    }
    
    return;
}

kern_return_t _io_pm_set_value_int(
    mach_port_t   server,
    audit_token_t token,
    int           selector,
    int           inValue,
    int           *result)
{
    uid_t   callerUID;
    audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, 0, 0, NULL, NULL);
    
    *result = kIOReturnSuccess;
    switch (selector) {
    case kIOPMSetNoPoll:
        BatterySetNoPoll(inValue ? true:false);
        break;

    case kIOPMSetAssertionActivityLog:
        setAssertionActivityLog(inValue);
        break;

    case kIOPMSetAssertionActivityAggregate:
        setAssertionActivityAggregate(inValue);
        break;

    case kIOPMSetReservePowerMode:
        if (!auditTokenHasEntitlement(token, kIOPMReservePwrCtrlEntitlement))
            *result = kIOReturnNotPrivileged;
        else 
            *result = setReservePwrMode(inValue);

    default:
        break;
    }
    return KERN_SUCCESS;
}


kern_return_t _io_pm_get_value_int(
    mach_port_t   server,
    audit_token_t token,
    int           selector,
    int           *outValue)
{
    uid_t   callerUID;
    audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, 0, 0, NULL, NULL);

    *outValue = 0;
    
    switch(selector)
    {
#if !TARGET_OS_EMBEDDED
      case kIOPMGetSilentRunningInfo:
         if ( smcSilentRunningSupport( ))
            *outValue = 1;
         else 
             *outValue = 0;
         break;
        
     case kIOPMMT2Bookmark:
            if (0 == callerUID)
            {
                mt2PublishReports();
                *outValue = 0;
            } else {
                *outValue = 1;
            }
          break;
    case kIOPMDarkWakeThermalEventCount:
            *outValue = _darkWakeThermalEventCount;
        break;
#if TCPKEEPALIVE
    case kIOPMTCPKeepAliveExpirationOverride:
            if (gTCPKeepAlive) {
                *outValue = gTCPKeepAlive->overrideSec;
            }
        break;
            
    case kIOPMTCPKeepAliveIsActive:
            if (gTCPKeepAlive) {
                *outValue = (getTCPKeepAliveState(NULL, 0) == kActive) ? true : false;
            }
            break;
#endif
            
#endif
      default:
         *outValue = 0;
         break;

    }
    return KERN_SUCCESS;
}



kern_return_t _io_pm_force_active_settings(
    mach_port_t                 server,
    audit_token_t               token,
    vm_offset_t                 settings_ptr,
    mach_msg_type_number_t      settings_len,
    int                         *result)
{
    void                    *settings_buf = (void *)settings_ptr;
    CFDictionaryRef         force_settings = NULL;
    uid_t                   callerUID;
    
    audit_token_to_au32(token, NULL, &callerUID, NULL, NULL, NULL, NULL, NULL, NULL);

    if (0 != callerUID) {
        // Caller must be root
        *result = kIOReturnNotPrivileged;
    } else {
        force_settings = (CFDictionaryRef)IOCFUnserialize(settings_buf, 0, 0, 0);

        if(isA_CFDictionary(force_settings)) 
        {
            *result = _activateForcedSettings(force_settings);
        } else {
            *result = kIOReturnBadArgument;
        }
        
        if(force_settings) 
        {
            CFRelease(force_settings);
        }
    }
        
    // deallocate client's memory
    vm_deallocate(mach_task_self(), (vm_address_t)settings_ptr, settings_len);

    return KERN_SUCCESS;
}

kern_return_t _io_pm_set_active_profile(
    mach_port_t         server,
    audit_token_t       token,
    vm_offset_t         profiles_ptr,
    mach_msg_type_number_t    profiles_len,
    int                 *result)
{
    void                *profiles_buf = (void *)profiles_ptr;
    CFDictionaryRef     power_profiles = NULL;
    uid_t               callerUID;
    gid_t               callerGID;
    
    audit_token_to_au32(token, NULL, &callerUID, &callerGID, NULL, NULL, NULL, NULL, NULL);
    
    power_profiles = (CFDictionaryRef)IOCFUnserialize(profiles_buf, 0, 0, 0);
    if(isA_CFDictionary(power_profiles)) {
        *result = _IOPMSetActivePowerProfilesRequiresRoot(power_profiles, callerUID, callerGID);
        CFRelease(power_profiles);
    } else if(power_profiles) {
        CFRelease(power_profiles);
    }

    // deallocate client's memory
    vm_deallocate(mach_task_self(), (vm_address_t)profiles_ptr, profiles_len);

    return KERN_SUCCESS;
}




/* initializeInteresteNotifications
 *
 * Sets up the notification of general interest from the RootDomain
 */
static void
initializeInterestNotifications()
{
    IONotificationPortRef       notify_port = 0;
    io_iterator_t               battery_iter = 0;
    io_iterator_t               display_iter = 0;
    CFRunLoopSourceRef          rlser = 0;
    
    kern_return_t               kr;
    
    /* Notifier */
    notify_port = IONotificationPortCreate(0);
    rlser = IONotificationPortGetRunLoopSource(notify_port);
    if(!rlser) return;
    CFRunLoopAddSource(CFRunLoopGetCurrent(), rlser, kCFRunLoopDefaultMode);


    kr = IOServiceAddMatchingNotification(
                                notify_port,
                                kIOFirstMatchNotification,
                                IOServiceMatching("IOPMPowerSource"),
                                ioregBatteryMatch,
                                (void *)notify_port,
                                &battery_iter);
    if(KERN_SUCCESS == kr)
    {
        // Install notifications on existing instances.
        ioregBatteryMatch((void *)notify_port, battery_iter);
    }

    kr = IOServiceAddMatchingNotification(
                                notify_port,
                                kIOFirstMatchNotification,
                                IOServiceMatching("IODisplayWrangler"),
                                displayMatched,
                                (void *)notify_port,
                                &display_iter);
    if(KERN_SUCCESS == kr) 
    {
        // Install notifications on existing instances.
        displayMatched((void *)notify_port, display_iter);
    }
    else {
        asl_log(NULL, NULL, ASL_LEVEL_ERR, 
                "Failed to match DisplayWrangler(0x%x)\n", kr);
    }
    
    // Listen for Power devices and Battery Systems to start ioupsd
    initializeHIDInterestNotifications(kIOPowerDeviceUsageKey, notify_port);
    initializeHIDInterestNotifications(kIOBatterySystemUsageKey, notify_port);
}

static void
initializeHIDInterestNotifications(int usagePage,
                                   IONotificationPortRef notify_port)
{
    CFMutableDictionaryRef matchingDict = IOServiceMatching(kIOHIDDeviceKey);
    if (!matchingDict) {
        return;
    }

    // We need to box the Usage Page value up into a CFNumber... sorry bout that
    CFNumberRef cfUsagePageKey = CFNumberCreate(kCFAllocatorDefault,
                                                kCFNumberIntType,
                                                &usagePage);
    if (!cfUsagePageKey) {
        CFRelease(matchingDict);
        return;
    }

    CFDictionarySetValue(matchingDict,
                         CFSTR(kIOHIDPrimaryUsagePageKey),
                         cfUsagePageKey);
    CFRelease(cfUsagePageKey);
    
    
    // Now set up a notification to be called when a device is first matched by
    // I/O Kit. Note that this will not catch any devices that were already
    // plugged in so we take care of those later.
    kern_return_t kr =
            IOServiceAddMatchingNotification(notify_port,
                                             kIOFirstMatchNotification,
                                             matchingDict,
                                             UPSDeviceAdded,
                                             NULL,
                                             &_ups_added_noteref);

    matchingDict = 0; // reference consumed by AddMatchingNotification
    if ( kr == kIOReturnSuccess ) {
        // Check for existing matching devices and launch ioupsd if present.
        UPSDeviceAdded( NULL, _ups_added_noteref);
    }
}

/* initializeTimezoneChangeNotifications
 *
 * Sets up the tz notifications that we re-broadcast to all interested
 * kernel clients listening via PMSettings
 */
static void
initializeTimezoneChangeNotifications(void)
{
    CFNotificationCenterRef         distNoteCenter = NULL;
    
    gTZNotificationNameString = CFStringCreateWithCString(
                        kCFAllocatorDefault,
                        "NSSystemTimeZoneDidChangeDistributedNotification",
                        kCFStringEncodingMacRoman);

#if TARGET_OS_EMBEDDED                                      
    distNoteCenter = CFNotificationCenterGetDarwinNotifyCenter();
#else
    distNoteCenter = CFNotificationCenterGetDistributedCenter();
#endif
    if(distNoteCenter)
    {
        CFNotificationCenterAddObserver(
                       distNoteCenter, 
                       NULL, 
                       timeZoneChangedCallBack, 
                       gTZNotificationNameString,
                       NULL, 
                       CFNotificationSuspensionBehaviorDeliverImmediately);
    }

    // Boot time - tell clients what our timezone offset is
    broadcastGMTOffset(); 
}

static void initializeCalendarResyncNotification(void)
{
    CFMachPortRef               mpref = NULL;
    CFRunLoopSourceRef          mpsrc = NULL;
    mach_port_t                 nport, tport;
    kern_return_t               result;

    result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET, &nport);
    if (result != KERN_SUCCESS) {
        goto exit;
    }
    result = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &tport);
    if (result != KERN_SUCCESS) {
        goto exit;
    }
    result = mach_port_move_member(mach_task_self(), tport, nport);
    if (result != KERN_SUCCESS) {
        goto exit;
    }
    result = host_request_notification(mach_host_self(), HOST_NOTIFY_CALENDAR_CHANGE, tport);
    if (result != KERN_SUCCESS) {
        goto exit;
    }

    mpref = _SC_CFMachPortCreateWithPort("PowerManagement/calendarResync", tport, calendarRTCDidResync, NULL);
    if (mpref) {
        mpsrc = CFMachPortCreateRunLoopSource(0, mpref, 0);
        if (mpsrc) {
            CFRunLoopAddSource(CFRunLoopGetCurrent(), mpsrc, kCFRunLoopDefaultMode);
            CFRelease(mpsrc);
        }
        CFRelease(mpref);
    }

exit:
    return;
}

static void calendarRTCDidResync_getSMCWakeInterval(void)
{
#if !TARGET_OS_EMBEDDED
    uint16_t            wakeup_smc_result = 0;
    IOReturn            ret = kIOReturnSuccess;
#endif
    CFAbsoluteTime      lastWakeTime;
    struct timeval      lastSleepTime;
    size_t              len = sizeof(struct timeval);

    // Capture this as early as possible
    lastWakeTime = CFAbsoluteTimeGetCurrent();
        
    if (!gExpectingWakeFromSleepClockResync) {
        // This is a non-wake-from-sleep clock resync, so we'll ignore it.
        goto exit;
    }

    if (sysctlbyname("kern.sleeptime", &lastSleepTime, &len, NULL, 0) ||
        ((gLastSleepTime.tv_sec  == lastSleepTime.tv_sec) &&
         (gLastSleepTime.tv_usec == lastSleepTime.tv_usec)))
    {
        // This is a clock resync after sleep has started but before
        // platform sleep.
        goto exit;
    }

    // if needed, init standalone memory for last wake time data
    if (!gLastSMCS3S0WakeInterval) {
        size_t bufSize;
        
        bufSize = sizeof(*gLastWakeTime) + sizeof(*gLastSMCS3S0WakeInterval);
        if (0 != vm_allocate(mach_task_self(), (void*)&gLastWakeTime,
                             bufSize, VM_FLAGS_ANYWHERE)) {
            return;
        }
        gLastSMCS3S0WakeInterval = gLastWakeTime + 1;
    } else {
        // validate pointers allocated earlier
        if (!gLastWakeTime || !gLastSMCS3S0WakeInterval)
            return;
    }
    
    // This is a wake-from-sleep resync, so commit the last wake time
    *gLastWakeTime = lastWakeTime;
    *gLastSMCS3S0WakeInterval = 0;
    gExpectingWakeFromSleepClockResync = false;
    
    // Re-enable battery time remaining calculations
    (void) BatteryTimeRemainingRTCDidResync();
    
#if !TARGET_OS_EMBEDDED
    if (!gSMCSupportsWakeupTimer) {
        // This system's SMC doesn't support a wakeup time, so we're done
        goto exit;
    }
    
    if (gLastWakeTimeToken < 0)
        notify_register_check(kIOPMLastWakeTimeString, &gLastWakeTimeToken);
    if (gLastSMCS3S0WakeIntervalToken < 0)
        notify_register_check(kIOPMLastWakeTimeSMCDataString, &gLastSMCS3S0WakeIntervalToken);

    // Read SMC key for precise timing between when the wake event physically occurred
    // and now (i.e. the moment we read the key).
    // - SMC key returns the delta in tens of milliseconds
    ret = _smcWakeTimerGetResults(&wakeup_smc_result);
    if ((ret != kIOReturnSuccess) || (wakeup_smc_result == 0))
    {
        if (kIOReturnNotFound == ret) {
            gSMCSupportsWakeupTimer = false;
        }
        goto exit;
    }
    // re-sample the current time closer to the SMC key read
    *gLastWakeTime = CFAbsoluteTimeGetCurrent();
    
    // convert 10x msecs to (double)seconds
    *gLastSMCS3S0WakeInterval = ((double)wakeup_smc_result / 100.0);
    
    // And we adjust backwards to determine the real time of physical wake.
    *gLastWakeTime -= *gLastSMCS3S0WakeInterval;

    if (gLastWakeTimeToken >= 0) {
        union {
            uint64_t        u64;
            CFAbsoluteTime  time;
        } nstate;
        __Check_Compile_Time(sizeof(nstate.time) <= sizeof(nstate.u64));

        nstate.u64 = 0;
        nstate.time = *gLastWakeTime;
        notify_set_state(gLastWakeTimeToken, nstate.u64);
    }
    if (gLastSMCS3S0WakeIntervalToken >= 0)
        notify_set_state(gLastSMCS3S0WakeIntervalToken, wakeup_smc_result);
#endif
exit:
    return;
}

static void calendarRTCDidResync(CFMachPortRef port, void *msg, CFIndex size, void *info)
{
    mach_msg_header_t   *header = (mach_msg_header_t *)msg;

    if (!header || HOST_CALENDAR_CHANGED_REPLYID != header->msgh_id) {
        return;
    }
    
    // renew our request for calendar change notification
    (void) host_request_notification(mach_host_self(), HOST_NOTIFY_CALENDAR_CHANGE,
                                     header->msgh_local_port);

    calendarRTCDidResync_getSMCWakeInterval();
    AutoWakeCalendarChange();
    
    return;
}

/* MIG CALL
 * Returns last wake time to a querulous process
 */
kern_return_t _io_pm_last_wake_time(
    mach_port_t             server,
    vm_offset_t             *out_wake_data,
    mach_msg_type_number_t  *out_wake_len,
    vm_offset_t             *out_delta_data,
    mach_msg_type_number_t  *out_delta_len,
    int                     *return_val)
{
    *out_wake_len = 0;
    *out_delta_len = 0;
    *return_val = kIOReturnInvalid;

    if (gExpectingWakeFromSleepClockResync) {
        *return_val = kIOReturnNotReady;
        return KERN_SUCCESS;
    }

#if !TARGET_OS_EMBEDDED
    if (!gSMCSupportsWakeupTimer) {
        *return_val = kIOReturnNotFound;
        return KERN_SUCCESS;
    };
#endif

    *out_wake_data = (vm_offset_t)gLastWakeTime;
    *out_wake_len = sizeof(*gLastWakeTime);
    *out_delta_data = (vm_offset_t)gLastSMCS3S0WakeInterval;
    *out_delta_len = sizeof(*gLastSMCS3S0WakeInterval);

    *return_val = kIOReturnSuccess;

    return KERN_SUCCESS;
}


/* displayMatched
 *
 * Notification fires when IODisplayWranger object is created in the IORegistry.
 *
 */
static void displayMatched(
    void *note_port_in, 
    io_iterator_t iter)
{
    IONotificationPortRef       note_port = (IONotificationPortRef)note_port_in;
    io_service_t                wrangler = MACH_PORT_NULL;
    io_object_t                 dimming_notification_object = MACH_PORT_NULL;
    
    if((wrangler = (io_registry_entry_t)IOIteratorNext(iter))) 
    {        
        IOServiceAddInterestNotification(
                    note_port, 
                    wrangler, 
                    kIOGeneralInterest, 
                    displayPowerStateChange,
                    NULL, 
                    &dimming_notification_object);

        IOObjectRelease(wrangler);
    }

}


static void 
initializeShutdownNotifications(void)
{
    CFMachPortRef       gNotifyMachPort = NULL;
    CFRunLoopSourceRef  gNotifyMachPortRLS = NULL;
    mach_port_t         our_port = MACH_PORT_NULL;

    // Tell the kernel that we are NOT shutting down at the moment, since
    // configd is just launching now.
    // Why: if configd crashed with "System Shutdown" == kCFbooleanTrue, reset
    // it now as the situation may no longer apply.
    _setRootDomainProperty(CFSTR("System Shutdown"), kCFBooleanFalse);

    /* * * * * * * * * * * * * */
    
    // Sneak in our registration for CPU power notifications here; to piggy-back
    // with the other mach port registrations for LW.
    notify_register_mach_port( 
                        kIOPMCPUPowerNotificationKey, 
                        &our_port, 
                        0, /* flags */ 
                        &gCPUPowerNotificationToken);
    
    
    notify_register_mach_port( 
                        kLWShutdowntInitiated, 
                        &our_port, 
                        NOTIFY_REUSE, /* flags */ 
                        &lwNotify.shutdown);

    notify_register_mach_port( 
                        kLWRestartInitiated, 
                        &our_port, 
                        NOTIFY_REUSE, /* flags */ 
                        &lwNotify.restart);

    notify_register_mach_port( 
                        kLWLogoutCancelled, 
                        &our_port, 
                        NOTIFY_REUSE, /* flags */ 
                        &lwNotify.cancel);

    notify_register_mach_port( 
                        kLWLogoutPointOfNoReturn, 
                        &our_port, 
                        NOTIFY_REUSE, /* flags */ 
                        &lwNotify.pointofnoreturn);
    notify_register_mach_port( 
                        kLWSULogoutInitiated,
                        &our_port, 
                        NOTIFY_REUSE, /* flags */ 
                        &lwNotify.su);

    /* * * * * * * * * * * * * */

    gNotifyMachPort = _SC_CFMachPortCreateWithPort(
                                "PowerManagement/shutdown",
                                our_port,
                                lwShutdownCallback,
                                NULL);

    if (gNotifyMachPort) {
        gNotifyMachPortRLS = CFMachPortCreateRunLoopSource(0, gNotifyMachPort, 0);
        if (gNotifyMachPortRLS) {        
            CFRunLoopAddSource(CFRunLoopGetCurrent(), gNotifyMachPortRLS, kCFRunLoopDefaultMode);
            CFRelease(gNotifyMachPortRLS);
        }
        CFRelease(gNotifyMachPort);
    }
}

#if !TARGET_OS_EMBEDDED
static void handleDWThermalMsg(CFStringRef wakeType)
{
    CFMutableDictionaryRef options = NULL;

    if (wakeType == NULL)
        getPlatformWakeReason(NULL, &wakeType);

    if ( (isA_BTMtnceWake() || isA_SleepSrvcWake()) && !isA_NotificationDisplayWake() && 
               (CFEqual(wakeType, kIOPMRootDomainWakeTypeMaintenance) || 
                        CFEqual(wakeType, kIOPMRootDomainWakeTypeSleepService))
#if TCPKEEPALIVE
            && !((getTCPKeepAliveState(NULL, 0) == kActive) && checkForActivesByType(kInteractivePushServiceType)) ) {
#else
        ) {
#endif
        
        // If system woke up for PowerNap and system is in a power nap wake, without any notifications
        // being displayed, then let system go to sleep
        options = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, 
                            &kCFTypeDictionaryValueCallBacks);
        if (options) {
            CFDictionarySetValue(options, CFSTR("Sleep Reason"), CFSTR(kIOPMDarkWakeThermalEmergencyKey));
        }
        IOPMSleepSystemWithOptions(getRootDomainConnect(), options);
        if (options) CFRelease(options);
    }
    else {
        // For all other cases, let system run in non-silent running mode
        _unclamp_silent_running(true);
        logASLMessageIgnoredDWTEmergency();
    }
}
#endif


static void 
RootDomainInterest(
    void *refcon, 
    io_service_t root_domain, 
    natural_t messageType, 
    void *messageArgument)
{
    static CFStringRef  _uuidString = NULL;
#if !TARGET_OS_EMBEDDED
    CFStringRef wakeReason = NULL, wakeType = NULL;
#endif
    
    if (messageType == kIOPMMessageDriverAssertionsChanged)
    {
        CFNumberRef     driverAssertions = 0;
        uint32_t        _driverAssertions = 0;

        // Read driver assertion status
        driverAssertions = IORegistryEntryCreateCFProperty(getRootDomain(), CFSTR(kIOPMAssertionsDriverKey), 0, 0);
        
        if (driverAssertions) {
            CFNumberGetValue(driverAssertions, kCFNumberIntType, &_driverAssertions);
            _PMAssertionsDriverAssertionsHaveChanged(_driverAssertions);
            CFRelease(driverAssertions);
        }
    }
    
    if (messageType == kIOPMMessageSystemPowerEventOccurred)
    {
        // Let System Events know about just-occurred thermal state change
        
        PMSystemEventsRootDomainInterest();
    }

#if !TARGET_OS_EMBEDDED
    if(messageType == kIOPMMessageDarkWakeThermalEmergency)
    {
        mt2RecordThermalEvent(kThermalStateSleepRequest);
        _darkWakeThermalEventCount++;

        getPlatformWakeReason(&wakeReason, &wakeType);
        if (CFEqual(wakeReason, CFSTR("")) && CFEqual(wakeType, CFSTR("")))
        {
            // Thermal emergency msg is received too early before wake type is 
            // determined. Delay the handler for a short handler until we know
            // the wake type
            if (gDWTMsgDispatch)
                dispatch_suspend(gDWTMsgDispatch);
            else {
                gDWTMsgDispatch = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0,
                        0, dispatch_get_main_queue()); 
                dispatch_source_set_event_handler(gDWTMsgDispatch, ^{
                    handleDWThermalMsg(NULL);
                });
                    
                dispatch_source_set_cancel_handler(gDWTMsgDispatch, ^{
                    if (gDWTMsgDispatch) {
                        dispatch_release(gDWTMsgDispatch);
                        gDWTMsgDispatch = 0;
                    }
                }); 
            }

            dispatch_source_set_timer(gDWTMsgDispatch, 
                    dispatch_walltime(NULL, kDWTMsgHandlerDelay * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0);
            dispatch_resume(gDWTMsgDispatch);
        }
        else
        {
            handleDWThermalMsg(wakeType);
            CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode,
                        ^{ logASLAssertionTypeSummary(kInteractivePushServiceType);});
            CFRunLoopWakeUp(_getPMRunLoop());
        }

    }
#endif

    if (messageType == kIOPMMessageFeatureChange)
    {
        // Let PMSettings code know that some settings may have been
        // added or removed.
    
        PMSettingsSupportedPrefsListHasChanged();
    }
    
    if (messageType == kIOPMMessageSleepWakeUUIDChange)
    {
        if (kIOPMMessageSleepWakeUUIDSet == messageArgument)
        {
            // We keep a copy of the newly published UUID string
            _uuidString = IOPMSleepWakeCopyUUID();

            // xnu kernel PM has just published a sleep/Wake UUID. 
            // We must replenish it with a new one (which we generate in user space)
            // Kernel PM will use the UUID we provide here on the next sleep/wake event.
            pushNewSleepWakeUUID();

        } else
        if (kIOPMMessageSleepWakeUUIDCleared == messageArgument)
        {
            // UUID cleared
            if (_uuidString) {
                // We're ready to parse out the newly acquired Power log and 
                // package events that pertain to the current UUID
                CFRelease(_uuidString);
                _uuidString = NULL;
            }
            
            // The kernel will have begun using its cached UUID (the one that we just stored)
            // for its power events log. We need to replenish its (now empty) cache with another 
            // UUID, which in turn will get used when the current one expires

            pushNewSleepWakeUUID();

        }    
    }

    if (messageType == kIOPMMessageUserIsActiveChanged)
    {
        userActiveHandleRootDomainActivity();
    }
}

static void 
initializeRootDomainInterestNotifications(void)
{
    IONotificationPortRef       note_port = MACH_PORT_NULL;
    CFRunLoopSourceRef          runLoopSrc = NULL;
    io_service_t                root_domain = MACH_PORT_NULL;
    io_object_t                 notification_object = MACH_PORT_NULL;
    IOReturn                    ret;

    root_domain = IORegistryEntryFromPath(kIOMasterPortDefault, 
                            kIOPowerPlane ":/IOPowerConnection/IOPMrootDomain");

    if(!root_domain) return;
        
    note_port = IONotificationPortCreate(MACH_PORT_NULL);
    if(!note_port) goto exit;
    
    ret = IOServiceAddInterestNotification(note_port, root_domain, 
                kIOGeneralInterest, RootDomainInterest,
                NULL, &notification_object);
    if (ret != kIOReturnSuccess) goto exit;
    
    runLoopSrc = IONotificationPortGetRunLoopSource(note_port);
    
    if (runLoopSrc)
    {
        CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSrc, kCFRunLoopDefaultMode);
    }
    
exit:
    // Do not release notification_object, would uninstall notification
    // Do not release runLoopSrc because it's 'owned' by note_port, and both
    // must be kept around to receive this notification
    if(MACH_PORT_NULL != root_domain) IOObjectRelease(root_domain);
}

#if !TARGET_OS_EMBEDDED
static void initializeUserNotifications(void)
{
    SCDynamicStoreRef   localStore = _getSharedPMDynamicStore();
    CFArrayRef          keys = NULL;

    gConsoleNotifyKey = SCDynamicStoreKeyCreateConsoleUser(NULL);
    if (gConsoleNotifyKey)
    {
        keys = CFArrayCreate(NULL, (const void **)&gConsoleNotifyKey, 
                                1, &kCFTypeArrayCallBacks);

        if (keys) {
            SCDynamicStoreSetNotificationKeys(localStore, keys, NULL);
            CFRelease(keys);
        }
    }
                
    SystemLoadUserStateHasChanged();
}

static void enableSleepWakeWdog()
{
    io_service_t                rootDomainService = IO_OBJECT_NULL;
    io_connect_t                rotDomainConnect = IO_OBJECT_NULL;
    kern_return_t               kr = 0;
    IOReturn                    ret;

    // Check if system supports NTS
    if (IONoteToSelfSupported() == false) 
        return;

    // Find it
    rootDomainService = getRootDomain();
    if (IO_OBJECT_NULL == rootDomainService) {
        goto exit;
    }

    // Open it
    kr = IOServiceOpen(rootDomainService, mach_task_self(), 0, &rotDomainConnect);    
    if (KERN_SUCCESS != kr) {
        goto exit;    
    }
    
    ret = IOConnectCallMethod(rotDomainConnect, kPMSleepWakeWatchdogEnable, 
                    NULL, 0, 
                    NULL, 0, NULL, 
                    NULL, NULL, NULL);

    if (kIOReturnSuccess != ret)
    {
        goto exit;
    }

exit:
    if (IO_OBJECT_NULL != rotDomainConnect)
        IOServiceClose(rotDomainConnect);
 
}

#endif

static void initializeSleepWakeNotifications(void)
{
    IONotificationPortRef           notify;
    io_object_t                     anIterator;

    _pm_ack_port = IORegisterForSystemPower(0, &notify, 
                                    SleepWakeCallback, &anIterator);
 
    if ( _pm_ack_port != MACH_PORT_NULL ) {
        if(notify) CFRunLoopAddSource(CFRunLoopGetCurrent(),
                            IONotificationPortGetRunLoopSource(notify),
                            kCFRunLoopDefaultMode);
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值