#include "powermanagementServer.h" // mig generated
#include <asl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <mach/mach.h>
#include <mach/message.h>
#include <notify.h>
#include <bsm/libbsm.h>
#include <IOKit/pwr_mgt/IOPM.h>
#include <libproc.h>
#include "PrivateLib.h"
#include "PMConnection.h"
#include "AutoWakeScheduler.h"
#include "RepeatingAutoWake.h"
#include "PMAssertions.h"
#include "PMStore.h"
#include "SystemLoad.h"
#if !TARGET_OS_EMBEDDED
#include "TTYKeepAwake.h"
#include "PMSettings.h"
#endif
#define kSleepState 0x01
#define kDarkWakeState 0x02
#define kDarkWakeForBTState 0x04
#define kDarkWakeForSSState 0x08
#define kDarkWakeForMntceState 0x10
#define kDarkWakeForServerState 0x20
#define kFullWakeState 0x40
#define kPowerStateMask (kSleepState|kDarkWakeState|kDarkWakeForBTState| \
kDarkWakeForSSState|kDarkWakeForMntceState|kDarkWakeForServerState|kFullWakeState)
#ifndef kIOPMMaintenanceScheduleImmediate
#define kIOPMMaintenanceScheduleImmediate "MaintenanceImmediate"
#endif
enum {
kSleepWakeInterestBit = 1,
kMaintenanceInterestBit = 2
};
enum {
_kSleepStateBits = 0x0000,
_kOnStateBits = 0xFFFF
};
enum {
kChooseFullWake = 0,
kChooseMaintenance = 1,
kChooseSleepServiceWake = 2,
kChooseTimerPlugin = 3,
kChooseDWBTInterval = 4,
kChooseWakeTypeCount = 5
};
enum {
kSilentRunningOff = 0,
kSilentRunningOn = 1
};
static int const kMaxConnectionIDCount = 1000*1000*1000;
static int const kConnectionOffset = 1000;
static double const kPMConnectionNotifyTimeoutDefault = 25.0;
#if !TARGET_OS_EMBEDDED
static int kPMSleepDurationForBT = (30*60); #if LOG_SLEEPSERVICES
static int gNotifySleepServiceToken = 0;
#endif
#endif
static CFAbsoluteTime gWakeForDWBTInterval = 0;
static CFAbsoluteTime ts_nextPowerNap = 0;
static uint32_t gPowerState;
static io_service_t rootDomainService = IO_OBJECT_NULL;
static IOPMCapabilityBits gCurrentCapabilityBits = kIOPMCapabilityCPU | kIOPMCapabilityDisk
| kIOPMCapabilityNetwork | kIOPMCapabilityAudio | kIOPMCapabilityVideo;
extern CFMachPortRef pmServerMachPort;
typedef struct {
CFMutableArrayRef awaitingResponses;
CFRunLoopTimerRef awaitingResponsesTimeout;
CFAbsoluteTime allRepliedTime;
long kernelAcknowledgementID;
int notificationType;
int awaitingResponsesCount;
int awaitResponsesTimeoutSeconds;
int completedStatus; bool completed;
bool nextIsValid;
long nextKernelAcknowledgementID;
int nextInterestBits;
} PMResponseWrangler;
typedef struct {
mach_port_t notifyPort;
PMResponseWrangler *responseHandler;
CFStringRef callerName;
uint32_t uniqueID;
int callerPID;
IOPMCapabilityBits interestsBits;
bool notifyEnable;
} PMConnection;
typedef struct {
PMConnection *connection;
PMResponseWrangler *myResponseWrangler;
IOPMConnectionMessageToken token;
CFAbsoluteTime repliedWhen;
CFAbsoluteTime notifiedWhen;
CFAbsoluteTime maintenanceRequested;
CFAbsoluteTime timerPluginRequested;
CFAbsoluteTime sleepServiceRequested;
CFStringRef clientInfoString;
int sleepServiceCapTimeoutMS;
int notificationType;
bool replied;
bool timedout;
} PMResponse;
#define VALID_DATE(x) (x!=0.0)
static IOReturn createConnectionWithID(
PMConnection **);
static PMConnection *connectionForID(
uint32_t findMe);
static CFArrayRef createArrayOfConnectionsWithInterest(
int interestBitsNotify);
static PMResponseWrangler *connectionFireNotification(
int notificationType,
long kernelAcknowledgementID);
static void _sendMachMessage(
mach_port_t port,
mach_msg_id_t msg_id,
uint32_t payload_bits,
uint32_t payload_messagetoken);
static void checkResponses(PMResponseWrangler *wrangler);
static void PMScheduleWakeEventChooseBest(CFAbsoluteTime *pick);
static void responsesTimedOut(CFRunLoopTimerRef timer, void * info);
static void cleanupConnection(PMConnection *reap);
static void cleanupResponseWrangler(PMResponseWrangler *reap);
static void setSystemSleepStateTracking(IOPMCapabilityBits);
#if !TARGET_OS_EMBEDDED
static void scheduleSleepServiceCapTimerEnforcer(uint32_t cap_ms);
#endif
#if LOG_SLEEPSERVICES
static void logASLMessageSleepServiceBegins(long withCapTime);
__private_extern__ void logASLMessageSleepServiceTerminated(int forcedTimeoutCnt);
#endif
static void PMConnectionPowerCallBack(
void *port,
io_service_t rootdomainservice,
natural_t messageType,
void *messageData);
__private_extern__ void ClockSleepWakeNotification(IOPMCapabilityBits b,
IOPMCapabilityBits c);
bool smcSilentRunningSupport( );
static Boolean _CFArrayConnectionEquality(const void *value1, const void *value2)
{
const PMConnection *v1 = (const PMConnection *)value1;
const PMConnection *v2 = (const PMConnection *)value2;
return (v1->uniqueID == v2->uniqueID);
}
static CFArrayCallBacks _CFArrayConnectionCallBacks =
{ 0, NULL, NULL, NULL, _CFArrayConnectionEquality };
static CFArrayCallBacks _CFArrayVanillaCallBacks =
{ 0, NULL, NULL, NULL, NULL };
static CFMutableArrayRef gConnections = NULL;
static uint32_t globalConnectionIDTally = 0;
static io_connect_t gRootDomainConnect = IO_OBJECT_NULL;
static PMResponseWrangler * gLastResponseWrangler = NULL;
SleepServiceStruct gSleepService;
uint32_t gDebugFlags = 0;
uint32_t gCurrentSilentRunningState = kSilentRunningOff;
#define IS_DARK_CAPABILITIES(x) \
(BIT_IS_SET(x, kIOPMSystemCapabilityCPU) \
&& BIT_IS_NOT_SET(x, kIOPMSystemCapabilityGraphics|kIOPMSystemCapabilityAudio))
#define SYSTEM_WILL_WAKE(x) \
( BIT_IS_SET(capArgs->changeFlags, kIOPMSystemCapabilityWillChange) \
&& IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityCPU) )
#define SYSTEM_DID_WAKE(x) \
( BIT_IS_SET(capArgs->changeFlags, kIOPMSystemCapabilityDidChange) \
&& (IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityCPU) \
|| IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityGraphics) ) )
#define SYSTEM_WILL_SLEEP_TO_S0(x) \
((CAPABILITY_BIT_CHANGED(x->fromCapabilities, x->toCapabilities, kIOPMSystemCapabilityCPU)) \
&& BIT_IS_SET(x->changeFlags, kIOPMSystemCapabilityWillChange) \
&& BIT_IS_NOT_SET(x->toCapabilities, kIOPMSystemCapabilityCPU))
#define SYSTEM_WILL_SLEEP_TO_S0DARK(x) \
((CHANGED_CAP_BITS(x->fromCapabilities, x->toCapabilities) == (kIOPMSystemCapabilityGraphics|kIOPMSystemCapabilityAudio)) \
&& BIT_IS_SET(x->changeFlags, kIOPMSystemCapabilityWillChange) \
&& BIT_IS_NOT_SET(x->toCapabilities, kIOPMSystemCapabilityGraphics|kIOPMSystemCapabilityAudio))
__private_extern__
void PMConnection_prime()
{
io_object_t sleepWakeCallbackHandle = IO_OBJECT_NULL;
IONotificationPortRef notify = NULL;
kern_return_t kr = 0;
char *errorString = NULL;
bzero(&gSleepService, sizeof(gSleepService));
gConnections = CFArrayCreateMutable(kCFAllocatorDefault, 100, &_CFArrayConnectionCallBacks);
rootDomainService = getRootDomain();
if (IO_OBJECT_NULL == rootDomainService) {
errorString = "Could not find IOPMrootDomain";
goto error;
}
kr = IOServiceOpen(rootDomainService, mach_task_self(), 0, &gRootDomainConnect);
if (KERN_SUCCESS != kr) {
errorString = "Could not open IOPMrootDomain";
goto error;
}
notify = IONotificationPortCreate(MACH_PORT_NULL);
if (!notify) {
errorString = "Could not create IONotificationPort";
goto error;
}
kr = IOServiceAddInterestNotification(notify, rootDomainService, "IOPMSystemCapabilityInterest",
(IOServiceInterestCallback) PMConnectionPowerCallBack, NULL,
&sleepWakeCallbackHandle);
if (KERN_SUCCESS != kr) {
errorString = "Could not add interest notification kIOPMAppPowerStateInterest";
goto error;
}
CFRunLoopAddSource(CFRunLoopGetCurrent(),
IONotificationPortGetRunLoopSource(notify),
kCFRunLoopDefaultMode);
return;
error:
asl_log(NULL, NULL, ASL_LEVEL_ERR,
"PowerManagement: unable to register with kernel power management. %s %s",
errorString ? "Reason = : ":"", errorString ? errorString:"unknown");
return;
}
#pragma mark -
#pragma mark MIG
kern_return_t _io_pm_connection_create
(
mach_port_t server,
mach_port_t task_in,
string_t name,
int interests,
uint32_t *connection_id,
int *return_code
)
{
PMConnection *newConnection = NULL;
int task_pid;
createConnectionWithID(&newConnection);
if (!newConnection) {
*return_code = kIOReturnError;
goto exit;
}
if (KERN_SUCCESS == pid_for_task(task_in, &task_pid)) {
newConnection->callerPID = task_pid;
}
if (name && strlen(name)) {
newConnection->callerName = CFStringCreateWithCString(0, name, kCFStringEncodingUTF8);
}
newConnection->interestsBits = 0xFF;
*connection_id = newConnection->uniqueID;
*return_code = kIOReturnSuccess;
exit:
if (MACH_PORT_NULL != task_in)
{
__MACH_PORT_DEBUG(true, "_io_pm_connection_create dropping send right", task_in);
mach_port_deallocate(mach_task_self(), task_in);
}
return KERN_SUCCESS;
}
kern_return_t _io_pm_connection_schedule_notification(
mach_port_t server,
uint32_t connection_id,
mach_port_t notify_port_in,
int disable,
int *return_code)
{
PMConnection *connection = NULL;
mach_port_t oldNotify;
if (MACH_PORT_NULL == notify_port_in || NULL == return_code) {
if (return_code) *return_code = kIOReturnBadArgument;
goto exit;
}
__MACH_PORT_DEBUG(true, "_io_pm_connection_schedule_notification notify_port", notify_port_in);
*return_code = kIOReturnError;
connection = connectionForID(connection_id);
if (!connection) {
return kIOReturnNotFound;
}
connection->notifyEnable = (disable == 0);
if (!disable && (MACH_PORT_NULL == connection->notifyPort)) {
connection->notifyPort = notify_port_in;
mach_port_request_notification(
mach_task_self(), notify_port_in, MACH_NOTIFY_DEAD_NAME, 1, CFMachPortGetPort(pmServerMachPort), MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldNotify);
__MACH_PORT_DEBUG(true, "Registered dead name notification on notifyPort", notify_port_in);
} else {
mach_port_deallocate(mach_task_self(), notify_port_in);
}
*return_code = kIOReturnSuccess;
exit:
return KERN_SUCCESS;
}
kern_return_t _io_pm_connection_release
(
mach_port_t server,
uint32_t connection_id,
int *return_code
)
{
PMConnection *cleanMeUp = NULL;
cleanMeUp = connectionForID(connection_id);
if (cleanMeUp) {
cleanupConnection(cleanMeUp);
if (return_code)
*return_code = kIOReturnSuccess;
} else {
if (return_code)
*return_code = kIOReturnNotFound;
}
return KERN_SUCCESS;
}
static PMResponse *_io_pm_acknowledge_event_findOutstandingResponseForToken(PMConnection *connection, int token)
{
CFMutableArrayRef responsesTrackingList = NULL;
int responsesCount = 0;
PMResponse *checkResponse = NULL;
PMResponse *foundResponse = NULL;
int i;
if (!connection
|| !connection->responseHandler
|| !(responsesTrackingList = connection->responseHandler->awaitingResponses))
{
return NULL;
}
responsesCount = CFArrayGetCount(responsesTrackingList);
for (i=0; i<responsesCount; i++)
{
checkResponse = (PMResponse *)CFArrayGetValueAtIndex(responsesTrackingList, i);
if (checkResponse && (token == checkResponse->token)) {
foundResponse = checkResponse;
break;
}
}
return foundResponse;
}
static CFDictionaryRef _io_pm_connection_acknowledge_event_unpack_payload(
vm_offset_t ptr,
mach_msg_type_number_t len)
{
CFDataRef optionsAsData = NULL;
CFPropertyListRef optionsUnzipped = NULL;
CFDictionaryRef ackOptionsDict = NULL;
if ( ptr != 0 && len != 0)
{
optionsAsData = CFDataCreateWithBytesNoCopy(0, (const uint8_t *)ptr, len, kCFAllocatorNull);
if (optionsAsData)
{
optionsUnzipped = CFPropertyListCreateWithData(0, optionsAsData, kCFPropertyListImmutable, NULL, NULL);
ackOptionsDict = (CFDictionaryRef) isA_CFDictionary(optionsUnzipped);
CFRelease(optionsAsData);
}
}
return ackOptionsDict;
}
kern_return_t _io_pm_connection_acknowledge_event
(
mach_port_t server,
uint32_t connection_id,
int messageToken,
vm_offset_t options_ptr,
mach_msg_type_number_t options_len,
int *return_code
)
{
#if TARGET_OS_EMBEDDED
return KERN_FAILURE;
#else
PMConnection *connection = connectionForID(connection_id);
int timeIntervalMS;
PMResponse *foundResponse = NULL;
CFDictionaryRef ackOptionsDict = NULL;
CFDateRef requestDate = NULL;
CFNumberRef sleepServiceCapTimer = NULL;
if (!(foundResponse = _io_pm_acknowledge_event_findOutstandingResponseForToken(connection, messageToken))) {
*return_code = kIOReturnNotFound;
goto exit;
}
*return_code = kIOReturnSuccess;
foundResponse->repliedWhen = CFAbsoluteTimeGetCurrent();
foundResponse->replied = true;
timeIntervalMS = (int)((foundResponse->repliedWhen - foundResponse->notifiedWhen) * 1000);
if (timeIntervalMS > kAppResponseLogThresholdMS)
{
CFNumberRef timeIntervalNumber = CFNumberCreate(NULL, kCFNumberIntType, &timeIntervalMS);
logASLMessageApplicationResponse(kAppResponseLogSourcePMConnection, connection->callerName,
CFSTR(kIOPMStatsResponseSlow), timeIntervalNumber, foundResponse->notificationType);
CFRelease(timeIntervalNumber);
}
else if (gDebugFlags & kIOPMDebugLogCallbacks)
{
logASLMessageApplicationResponse(kAppResponseLogSourcePMConnection, connection->callerName,
CFSTR(kMsgTracerDomainAppResponse), 0, foundResponse->notificationType);
}
if ((ackOptionsDict = _io_pm_connection_acknowledge_event_unpack_payload(options_ptr, options_len)))
{
foundResponse->clientInfoString = CFDictionaryGetValue(ackOptionsDict, kIOPMAckClientInfoKey);
if (foundResponse->clientInfoString) {
CFRetain(foundResponse->clientInfoString);
}
requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckWakeDate));
if (!requestDate) {
requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckNetworkMaintenanceWakeDate));
}
if (requestDate
&& (kACPowered == _getPowerSource()))
{
foundResponse->maintenanceRequested = CFDateGetAbsoluteTime(requestDate);
}
requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckTimerPluginWakeDate));
if (requestDate && _DWBT_allowed())
{
foundResponse->timerPluginRequested = CFDateGetAbsoluteTime(requestDate);
if ( foundResponse->timerPluginRequested < ts_nextPowerNap )
foundResponse->timerPluginRequested = ts_nextPowerNap;
}
requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckSleepServiceDate));
if (requestDate && _SS_allowed())
{
foundResponse->sleepServiceRequested = CFDateGetAbsoluteTime(requestDate);
if ( foundResponse->sleepServiceRequested < ts_nextPowerNap )
foundResponse->sleepServiceRequested = ts_nextPowerNap;
}
sleepServiceCapTimer = isA_CFNumber(CFDictionaryGetValue(ackOptionsDict,
kIOPMAcknowledgeOptionSleepServiceCapTimeout));
if (sleepServiceCapTimer)
{
if ( !(foundResponse->notificationType & kIOPMCapabilityPushServiceTask) )
*return_code = kIOReturnBadArgument;
else if (_SS_allowed()) {
CFNumberGetValue(sleepServiceCapTimer, kCFNumberIntType, &foundResponse->sleepServiceCapTimeoutMS);
scheduleSleepServiceCapTimerEnforcer(foundResponse->sleepServiceCapTimeoutMS);
}
}
CFRelease(ackOptionsDict);
}
checkResponses(connection->responseHandler);
exit:
vm_deallocate(mach_task_self(), options_ptr, options_len);
return KERN_SUCCESS;
#endif
}
kern_return_t _io_pm_get_capability_bits(
mach_port_t server,
audit_token_t token,
uint32_t *capBits,
int *return_code )
{
*capBits = gCurrentCapabilityBits;
*return_code = kIOReturnSuccess;
return KERN_SUCCESS;
}
kern_return_t _io_pm_connection_copy_status
(
mach_port_t server,
int status_index,
vm_offset_t *status_data,
mach_msg_type_number_t *status_dataCnt,
int *return_val
)
{
return KERN_SUCCESS;
}
kern_return_t _io_pm_set_debug_flags(
mach_port_t server,
audit_token_t token,
uint32_t newFlags,
uint32_t *oldFlags,
int *return_code )
{
pid_t callerPID = -1;
uid_t callerUID = -1;
gid_t callerGID = -1;
audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, &callerGID, &callerPID, NULL, NULL);
if ( !(callerIsRoot(callerUID) || callerIsAdmin(callerUID, callerGID) ))
{
*return_code = kIOReturnNotPrivileged;
goto exit;
}
if (oldFlags)
*oldFlags = gDebugFlags;
gDebugFlags = newFlags;
*return_code = kIOReturnSuccess;
exit:
return KERN_SUCCESS;
}
kern_return_t _io_pm_set_bt_wake_interval
(
mach_port_t server,
audit_token_t token,
uint32_t newInterval,
uint32_t *oldInterval,
int *return_code
)
{
#if !TARGET_OS_EMBEDDED
pid_t callerPID = -1;
uid_t callerUID = -1;
gid_t callerGID = -1;
audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, &callerGID, &callerPID, NULL, NULL);
if ( !(callerIsRoot(callerUID) || callerIsAdmin(callerUID, callerGID) ))
{
*return_code = kIOReturnNotPrivileged;
goto exit;
}
if (oldInterval)
*oldInterval = kPMSleepDurationForBT;
kPMSleepDurationForBT = newInterval;
if ( newInterval ) {
_Set_SR_override(kPMSROverrideEnable);
}
else {
_Set_SR_override(kPMSROverrideDisable);
}
#endif
*return_code = kIOReturnSuccess;
exit:
return KERN_SUCCESS;
}
#pragma mark -
#pragma mark Connection
static void cleanupConnection(PMConnection *reap)
{
PMResponseWrangler *responseWrangler = NULL;
PMResponse *openResponse = NULL;
int allResponsesCount = 0;
int i;
int index;
CFRange connectionsRange =
CFRangeMake(0, CFArrayGetCount(gConnections));
if (MACH_PORT_NULL != reap->notifyPort)
{
__MACH_PORT_DEBUG(true, "IOPMConnection cleanupConnection drop notifyPort", reap->notifyPort);
mach_port_deallocate(mach_task_self(), reap->notifyPort);
reap->notifyPort = MACH_PORT_NULL;
}
if (reap->callerName) {
CFRelease(reap->callerName);
reap->callerName = NULL;
}
responseWrangler = reap->responseHandler;
if (responseWrangler && responseWrangler->awaitingResponses)
{
allResponsesCount = CFArrayGetCount(responseWrangler->awaitingResponses);
for (i=0; i<allResponsesCount; i++)
{
openResponse = (PMResponse *)CFArrayGetValueAtIndex(
responseWrangler->awaitingResponses, i);
if (openResponse && (openResponse->connection == reap)) {
openResponse->connection = NULL;
openResponse->replied = true;
openResponse->timedout = true;
break;
}
}
checkResponses(responseWrangler);
}
index = CFArrayGetFirstIndexOfValue(gConnections, connectionsRange, (void *)reap);
if (kCFNotFound != index) {
CFArrayRemoveValueAtIndex(gConnections, index);
}
free(reap);
return;
}
static void cleanupResponseWrangler(PMResponseWrangler *reap)
{
PMConnection *one_connection;
long nextAcknowledgementID;
int responseCount;
int connectionsCount;
int i;
int nextInterestBits;
bool nextIsValid;
if (!reap)
return;
if (!gConnections)
return;
nextInterestBits = reap->nextInterestBits;
nextAcknowledgementID = reap->nextKernelAcknowledgementID;
nextIsValid = reap->nextIsValid;
connectionsCount = CFArrayGetCount(gConnections);
for (i=0; i<connectionsCount; i++)
{
one_connection = (PMConnection *)CFArrayGetValueAtIndex(gConnections, i);
if (reap == one_connection->responseHandler)
{
one_connection->responseHandler = NULL;
}
}
if (reap->awaitingResponses)
{
responseCount = CFArrayGetCount(reap->awaitingResponses);
if (responseCount > 0)
{
for (i=0; i<responseCount; i++)
{
PMResponse *purgeMe = (PMResponse *)CFArrayGetValueAtIndex(reap->awaitingResponses, i);
if (purgeMe->clientInfoString)
CFRelease(purgeMe->clientInfoString);
free(purgeMe);
}
}
CFRelease(reap->awaitingResponses);
reap->awaitingResponses = NULL;
}
if (gLastResponseWrangler == reap) {
gLastResponseWrangler = NULL;
}
free(reap);
if (nextIsValid)
{
PMResponseWrangler * resp;
resp = connectionFireNotification(nextInterestBits, nextAcknowledgementID);
if (!resp)
{
if (nextAcknowledgementID)
IOAllowPowerChange(gRootDomainConnect, nextAcknowledgementID);
}
}
}
__private_extern__ bool PMConnectionHandleDeadName(mach_port_t deadPort)
{
PMConnection *one_connection = NULL;;
PMConnection *the_connection = NULL;
int connectionsCount = 0;
int i;
if (!gConnections)
return false;
connectionsCount = CFArrayGetCount(gConnections);
for (i=0; i<connectionsCount; i++)
{
one_connection = (PMConnection *)CFArrayGetValueAtIndex(gConnections, i);
if (one_connection && (deadPort == one_connection->notifyPort))
{
the_connection = one_connection;
break;
}
}
if (the_connection) {
cleanupConnection(the_connection);
return true;
} else {
return false;
}
}
static void setSystemSleepStateTracking(IOPMCapabilityBits capables)
{
SCDynamicStoreRef store = _getSharedPMDynamicStore();
CFStringRef key = NULL;
CFNumberRef capablesNum = NULL;
if (!store)
return;
key = SCDynamicStoreKeyCreate(0, CFSTR("%@%@"),
kSCDynamicStoreDomainState,
CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix));
if (!key)
return;
capablesNum = CFNumberCreate(0, kCFNumberIntType, &capables);
if (capablesNum) {
PMStoreSetValue(key, capablesNum);
CFRelease(capablesNum);
}
CFRelease(key);
}
#define IS_CAP_GAIN(c, f) \
((((c)->fromCapabilities & (f)) == 0) && \
(((c)->toCapabilities & (f)) != 0))
static bool PMConnectionPowerCallBack_HandleSleepForSSH(natural_t inMessageType, void *messageData)
{
bool allow_sleep = true;
if (kIOMessageCanSystemSleep == inMessageType)
{
#if !TARGET_OS_EMBEDDED
allow_sleep = TTYKeepAwakeConsiderAssertion( );
#endif
if (allow_sleep)
IOAllowPowerChange(gRootDomainConnect, (long)messageData);
else
IOCancelPowerChange(gRootDomainConnect, (long)messageData);
return true;
}
return false;
}
#pragma mark -
#pragma mark SleepServices
kern_return_t _io_pm_get_uuid(
mach_port_t server __unused,
int selector,
string_t out_uuid,
int *return_code)
{
if (kIOPMSleepServicesUUID == selector)
{
if (gSleepService.uuid)
{
if (CFStringGetCString(gSleepService.uuid, out_uuid, kPMMIGStringLength, kCFStringEncodingUTF8))
{
*return_code = kIOReturnSuccess;
return KERN_SUCCESS;
}
}
}
out_uuid[0] = '\0';
*return_code = kIOReturnNotFound;
return KERN_SUCCESS;
}
kern_return_t _io_pm_set_sleepservice_wake_time_cap(
mach_port_t server __unused,
audit_token_t token,
int cap_ms,
int *return_code)
{
#if !TARGET_OS_EMBEDDED
pid_t callerPID = -1;
uid_t callerUID = -1;
gid_t callerGID = -1;
audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, &callerGID, &callerPID, NULL, NULL);
if ( !callerIsRoot(callerUID) )
{
*return_code = kIOReturnNotPrivileged;
return KERN_SUCCESS;
}
if (gSleepService.capTime == 0 ||
!isA_SleepSrvcWake() )
{
*return_code = kIOReturnError;
return KERN_SUCCESS;
}
gSleepService.capTime = cap_ms;
setSleepServicesTimeCap(cap_ms);
#endif
*return_code = kIOReturnSuccess;
return KERN_SUCCESS;
}
#if !TARGET_OS_EMBEDDED
static void scheduleSleepServiceCapTimerEnforcer(uint32_t cap_ms)
{
CFMutableDictionaryRef assertionDescription = NULL;
if (checkForActivesByType(kPreventSleepIndex))
return;
gSleepService.capTime = (long)cap_ms;
setSleepServicesTimeCap(cap_ms);
if (gSleepService.uuid) {
CFRelease(gSleepService.uuid);
}
CFUUIDRef ssuuid = NULL;
ssuuid = CFUUIDCreate(0);
if (ssuuid) {
gSleepService.uuid = CFUUIDCreateString(0, ssuuid);
CFRelease(ssuuid);
}
gPowerState |= kDarkWakeForSSState;
configAssertionType(kPushServiceTaskIndex, false);
assertionDescription = _IOPMAssertionDescriptionCreate(
kIOPMAssertionTypeApplePushServiceTask,
CFSTR("Powerd - Wait for client pushService assertions"),
NULL, NULL, NULL,
10, kIOPMAssertionTimeoutActionRelease);
InternalCreateAssertion(assertionDescription, NULL);
CFRelease(assertionDescription);
if (!gSleepService.notifyToken) {
int status;
status = notify_register_check(kIOPMSleepServiceActiveNotifyName, &gSleepService.notifyToken);
if (NOTIFY_STATUS_OK != status) {
gSleepService.notifyToken = 0;
}
}
if (gSleepService.notifyToken)
{
notify_set_state(gSleepService.notifyToken, kIOPMSleepServiceActiveNotifyBit);
notify_post(kIOPMSleepServiceActiveNotifyName);
}
logASLMessageSleepServiceBegins(gSleepService.capTime);
if (!gNotifySleepServiceToken) {
int status;
status = notify_register_check(kIOPMSleepServiceActiveNotifyName, &gNotifySleepServiceToken);
if (NOTIFY_STATUS_OK != status) {
gNotifySleepServiceToken = 0;
}
}
if (gNotifySleepServiceToken)
{
notify_set_state(gNotifySleepServiceToken, kIOPMSleepServiceActiveNotifyBit);
notify_post(kIOPMSleepServiceActiveNotifyName);
}
}
#endif
#if LOG_SLEEPSERVICES
static void logASLMessageSleepServiceBegins(long withCapTime)
{
aslmsg beginMsg;
char strbuf[125];
beginMsg = asl_new(ASL_TYPE_MSG);
asl_set(beginMsg, kMsgTracerDomainKey, kMsgTracerDomainSleepServiceStarted);
if (_getUUIDString(strbuf, sizeof(strbuf))) {
asl_set(beginMsg, kMsgTracerUUIDKey, strbuf);
}
if (gSleepService.uuid
&& CFStringGetCString(gSleepService.uuid, strbuf, sizeof(strbuf), kCFStringEncodingUTF8))
{
asl_set(beginMsg, kMsgTracerUUID2Key, strbuf);
}
snprintf(strbuf, sizeof(strbuf), "%ld", withCapTime);
asl_set(beginMsg, kMsgTracerValueKey, strbuf);
snprintf(strbuf, sizeof(strbuf), "SleepService: window begins with cap time=%ld secs", withCapTime/1000);
asl_set(beginMsg, ASL_KEY_MSG, strbuf);
asl_set(beginMsg, kPMASLMessageKey, kPMASLMessageLogValue);
asl_set(beginMsg, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
asl_set(beginMsg, ASL_KEY_FACILITY, "internal");
asl_send(NULL, beginMsg);
asl_free(beginMsg);
}
__private_extern__ void logASLMessageSleepServiceTerminated(int forcedTimeoutCnt)
{
aslmsg endMsg;
char strUUID[100];
char strUUID2[100];
char valStr[30];
if ( (gPowerState & kDarkWakeForSSState) == 0)
return;
gPowerState &= ~kDarkWakeForSSState;
configAssertionType(kPushServiceTaskIndex, false);
endMsg = asl_new(ASL_TYPE_MSG);
asl_set(endMsg, kMsgTracerDomainKey, kMsgTracerDomainSleepServiceTerminated);
if (_getUUIDString(strUUID, sizeof(strUUID))) {
asl_set(endMsg, kMsgTracerUUIDKey, strUUID);
}
if (gSleepService.uuid
&& CFStringGetCString(gSleepService.uuid, strUUID2, sizeof(strUUID2), kCFStringEncodingUTF8))
{
asl_set(endMsg, kMsgTracerUUID2Key, strUUID2);
}
snprintf(valStr, sizeof(valStr), "%d", forcedTimeoutCnt);
asl_set(endMsg, kMsgTracerValueKey, valStr);
asl_set(endMsg, ASL_KEY_MSG, "SleepService: window has terminated.");
if (forcedTimeoutCnt == 0)
{
asl_set(endMsg, kMsgTracerSignatureKey, kMsgTracerSigSleepServiceExitClean);
} else {
asl_set(endMsg, kMsgTracerSignatureKey, kMsgTracerSigSleepServiceTimedOut);
}
asl_set(endMsg, kPMASLMessageKey, kPMASLMessageLogValue);
asl_set(endMsg, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
asl_set(endMsg, ASL_KEY_FACILITY, "internal");
asl_send(NULL, endMsg);
asl_free(endMsg);
}
#endif
__private_extern__ bool isA_DarkWakeState()
{
if (gPowerState & kDarkWakeState)
return true;
return false;
}
__private_extern__ bool isA_BTMtnceWake()
{
if (gPowerState & kDarkWakeForBTState)
return true;
return false;
}
__private_extern__ void set_SleepSrvcWake()
{
gPowerState |= kDarkWakeForSSState;
}
__private_extern__ bool isA_SleepSrvcWake()
{
if (gPowerState & kDarkWakeForSSState)
return true;
return false;
}
__private_extern__ void cancelPowerNapStates( )
{
#if !TARGET_OS_EMBEDDED
if (isA_SleepSrvcWake()) {
gSleepService.capTime = 0;
setSleepServicesTimeCap(0);
}
if (isA_BTMtnceWake() ) {
gPowerState &= ~kDarkWakeForBTState;
configAssertionType(kBackgroundTaskIndex, false);
}
SystemLoadSystemPowerStateHasChanged( );
#endif
}
#pragma mark -
#pragma mark Sleep/Wake
static void PMConnectionPowerCallBack(
void *port,
io_service_t rootdomainservice,
natural_t inMessageType,
void *messageData)
{
PMResponseWrangler *responseController = NULL;
IOPMCapabilityBits deliverCapabilityBits = 0;
const struct IOPMSystemCapabilityChangeParameters *capArgs;
#if !TARGET_OS_EMBEDDED
CFStringRef wakeType = NULL;
#endif
if (PMConnectionPowerCallBack_HandleSleepForSSH(inMessageType, messageData) ||
(kIOMessageSystemCapabilityChange != inMessageType) )
{
return;
}
capArgs = (const struct IOPMSystemCapabilityChangeParameters *)messageData;
AutoWakeCapabilitiesNotification(capArgs->fromCapabilities, capArgs->toCapabilities);
ClockSleepWakeNotification(capArgs->fromCapabilities, capArgs->toCapabilities);
if (IS_DARK_CAPABILITIES(capArgs->fromCapabilities)
&& !IS_DARK_CAPABILITIES(capArgs->toCapabilities))
{
mt2DarkWakeEnded();
}
if (SYSTEM_WILL_SLEEP_TO_S0(capArgs))
{
setSystemSleepStateTracking(0);
notify_post(kIOPMSystemPowerStateNotify);
#if !TARGET_OS_EMBEDDED
if ( ( gCurrentSilentRunningState == kSilentRunningOff ) ||
isA_BTMtnceWake() || isA_SleepSrvcWake() ){
ts_nextPowerNap = CFAbsoluteTimeGetCurrent() + kPMSleepDurationForBT;
if (_DWBT_allowed() && checkForEntriesByType(kBackgroundTaskIndex))
gWakeForDWBTInterval = ts_nextPowerNap;
}
#endif
cancelPowerNapStates();
gPowerState &= ~(kPowerStateMask ^ kDarkWakeForSSState);
gPowerState |= kSleepState;
logASLMessageSleep(kMsgTracerSigSuccess, NULL, NULL, kIsS0Sleep);
logASLMessageKernelApplicationResponses();
responseController = connectionFireNotification(_kSleepStateBits, (long)capArgs->notifyRef);
if (!responseController) {
PMScheduleWakeEventChooseBest(NULL);
IOAllowPowerChange(gRootDomainConnect, (long)capArgs->notifyRef);
}
return;
#if !TARGET_OS_EMBEDDED
} else if (SYSTEM_WILL_SLEEP_TO_S0DARK(capArgs))
{
if (systemBlockedInS0Dark()) {
logASLMessageSleep(kMsgTracerSigSuccess, NULL, NULL, kIsDarkWake);
}
gPowerState &= ~(kPowerStateMask ^ kDarkWakeForSSState);
gPowerState |= kDarkWakeState;
if (isA_SleepSrvcWake()) {
setSleepServicesTimeCap(0);
gSleepService.capTime = 0;
}
#endif
} else if (SYSTEM_DID_WAKE(capArgs))
{
if(smcSilentRunningSupport() )
gCurrentSilentRunningState = kSilentRunningOn;
cancelPowerNapStates();
if (IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityGraphics))
{
_unclamp_silent_running();
deliverCapabilityBits =
kIOPMCapabilityCPU | kIOPMCapabilityDisk
| kIOPMCapabilityNetwork | kIOPMCapabilityAudio | kIOPMCapabilityVideo;
if (BIT_IS_SET(capArgs->fromCapabilities, kIOPMSystemCapabilityCPU)) {
logASLMessageWake(kMsgTracerSigSuccess, NULL, NULL, kIsDarkToFullWake);
} else {
logASLMessageWake(kMsgTracerSigSuccess, NULL, NULL, kIsFullWake);
mt2RecordWakeEvent(kWakeStateFull);
}
gPowerState &= ~(kPowerStateMask ^ kDarkWakeForSSState);
gPowerState |= kFullWakeState;
} else if (IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityCPU))
{
deliverCapabilityBits =
kIOPMCapabilityCPU | kIOPMCapabilityDisk | kIOPMCapabilityNetwork;
logASLMessageWake(kMsgTracerSigSuccess, NULL, NULL, kIsDarkWake);
mt2RecordWakeEvent(kWakeStateDark);
gPowerState &= ~(kPowerStateMask ^ kDarkWakeForSSState);
gPowerState |= kDarkWakeState;
_ProxyAssertions(capArgs);
#if !TARGET_OS_EMBEDDED
if (checkForActivesByType(kPreventSleepIndex)) {
_unclamp_silent_running();
}
else if ( CFAbsoluteTimeGetCurrent() >= ts_nextPowerNap ) {
wakeType = _copyRootDomainProperty(CFSTR(kIOPMRootDomainWakeTypeKey));
if (isA_CFString(wakeType) && (CFEqual(wakeType, kIOPMRootDomainWakeTypeMaintenance) ||
CFEqual(wakeType, kIOPMRootDomainWakeTypeSleepService) ) ) {
if ( _DWBT_allowed() ) {
gWakeForDWBTInterval = 0;
gPowerState |= kDarkWakeForBTState;
configAssertionType(kBackgroundTaskIndex, false);
CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode,
^{ logASLAssertionTypeSummary(kBackgroundTaskIndex); });
CFRunLoopWakeUp(_getPMRunLoop());
deliverCapabilityBits |= (kIOPMCapabilityBackgroundTask | kIOPMCapabilityPushServiceTask);
}
else if (_SS_allowed() ) {
deliverCapabilityBits |= kIOPMCapabilityPushServiceTask;
}
}
if (wakeType) CFRelease(wakeType);
}
#endif
}
setSystemSleepStateTracking(deliverCapabilityBits);
notify_post(kIOPMSystemPowerStateNotify);
responseController = connectionFireNotification(deliverCapabilityBits, (long)capArgs->notifyRef);
if (!responseController) {
IOAllowPowerChange(gRootDomainConnect, (long)capArgs->notifyRef);
}
#if !TARGET_OS_EMBEDDED
SystemLoadSystemPowerStateHasChanged( );
#endif
return;
}
if (capArgs->notifyRef)
IOAllowPowerChange(gRootDomainConnect, capArgs->notifyRef);
}
#pragma mark -
#pragma mark Responses
static PMResponseWrangler *connectionFireNotification(
int interestBitsNotify,
long kernelAcknowledgementID)
{
static int lastInterestBits = 0xFFFFFFFF;
int affectedBits = 0;
CFArrayRef interested = NULL;
PMConnection *connection = NULL;
int interestedCount = 0;
uint32_t messageToken = 0;
uint16_t calloutCount = 0;
PMResponseWrangler *responseWrangler = NULL;
PMResponse *awaitThis = NULL;
if (gLastResponseWrangler)
{
gLastResponseWrangler->nextIsValid = true;
gLastResponseWrangler->nextInterestBits = interestBitsNotify;
gLastResponseWrangler->nextKernelAcknowledgementID = kernelAcknowledgementID;
return gLastResponseWrangler;
}
gCurrentCapabilityBits = interestBitsNotify;
affectedBits = interestBitsNotify ^ lastInterestBits;
lastInterestBits = interestBitsNotify;
interested = createArrayOfConnectionsWithInterest(affectedBits);
if (!interested) {
goto exit;
}
interestedCount = CFArrayGetCount(interested);
if (0 == interestedCount) {
goto exit;
}
responseWrangler = calloc(1, sizeof(PMResponseWrangler));
if (!responseWrangler) {
goto exit;
}
responseWrangler->notificationType = interestBitsNotify;
responseWrangler->awaitResponsesTimeoutSeconds = (int)kPMConnectionNotifyTimeoutDefault;
responseWrangler->kernelAcknowledgementID = kernelAcknowledgementID;
responseWrangler->awaitingResponses =
CFArrayCreateMutable(kCFAllocatorDefault, interestedCount, &_CFArrayVanillaCallBacks);
responseWrangler->awaitingResponsesCount = interestedCount;
for (calloutCount=0; calloutCount<interestedCount; calloutCount++)
{
connection = (PMConnection *)CFArrayGetValueAtIndex(interested, calloutCount);
if ((MACH_PORT_NULL == connection->notifyPort) ||
(false == connection->notifyEnable)) {
continue;
}
messageToken = (interestBitsNotify << 16)
| calloutCount;
connection->responseHandler = responseWrangler;
_sendMachMessage(connection->notifyPort,
0,
interestBitsNotify,
messageToken);
awaitThis = calloc(1, sizeof(PMResponse));
if (!awaitThis) {
goto exit;
}
awaitThis->token = messageToken;
awaitThis->connection = connection;
awaitThis->notificationType = interestBitsNotify;
awaitThis->myResponseWrangler = responseWrangler;
awaitThis->notifiedWhen = CFAbsoluteTimeGetCurrent();
CFArrayAppendValue(responseWrangler->awaitingResponses, awaitThis);
if (gDebugFlags & kIOPMDebugLogCallbacks)
logASLMessageAppNotify(awaitThis->connection->callerName, interestBitsNotify );
}
CFRunLoopTimerContext responseTimerContext =
{ 0, (void *)responseWrangler, NULL, NULL, NULL };
responseWrangler->awaitingResponsesTimeout =
CFRunLoopTimerCreate(0,
CFAbsoluteTimeGetCurrent() + responseWrangler->awaitResponsesTimeoutSeconds,
0.0, 0, 0, responsesTimedOut, &responseTimerContext);
if (responseWrangler->awaitingResponsesTimeout)
{
CFRunLoopAddTimer(CFRunLoopGetCurrent(),
responseWrangler->awaitingResponsesTimeout,
kCFRunLoopDefaultMode);
CFRelease(responseWrangler->awaitingResponsesTimeout);
}
exit:
if (interested)
CFRelease(interested);
if (responseWrangler)
gLastResponseWrangler = responseWrangler;
return responseWrangler;
}
static void responsesTimedOut(CFRunLoopTimerRef timer, void * info)
{
PMResponseWrangler *responseWrangler = (PMResponseWrangler *)info;
PMResponse *one_response = NULL;
int responsesCount = 0;
int i;
int tardyCount = 0;
if (!responseWrangler)
return;
responseWrangler->awaitingResponsesTimeout = NULL;
responsesCount = CFArrayGetCount(responseWrangler->awaitingResponses);
for (i=0; i<responsesCount; i++)
{
one_response = (PMResponse *)CFArrayGetValueAtIndex(
responseWrangler->awaitingResponses, i);
if (!one_response)
continue;
if (one_response->replied)
continue;
tardyCount++;
one_response->replied = true;
one_response->timedout = true;
one_response->repliedWhen = CFAbsoluteTimeGetCurrent();
int timeIntervalMS = (int)((one_response->repliedWhen - one_response->notifiedWhen) * 1000);
CFNumberRef timeIntervalNumber = CFNumberCreate(NULL, kCFNumberIntType, &timeIntervalMS);
logASLMessageApplicationResponse(
kAppResponseLogSourcePMConnection,
one_response->connection->callerName,
CFSTR(kIOPMStatsResponseTimedOut),
timeIntervalNumber,
one_response->notificationType);
if (timeIntervalNumber)
CFRelease(timeIntervalNumber);
}
checkResponses(responseWrangler);
}
#define kMsgPayloadCount 2
typedef struct {
mach_msg_header_t header;
mach_msg_body_t body;
uint32_t payload[kMsgPayloadCount];
} IOPMMessageStructure;
static void _sendMachMessage(
mach_port_t port,
mach_msg_id_t msg_id,
uint32_t payload_bits,
uint32_t payload_messagetoken)
{
kern_return_t status;
IOPMMessageStructure msg;
bzero(&msg, sizeof(msg));
msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0);
msg.header.msgh_size = sizeof(msg);
msg.header.msgh_remote_port = port;
msg.header.msgh_local_port = MACH_PORT_NULL;
msg.header.msgh_id = msg_id;
msg.body.msgh_descriptor_count = 0;
msg.payload[0] = payload_bits;
msg.payload[1] = payload_messagetoken;
status = mach_msg(&msg.header,
MACH_SEND_MSG | MACH_SEND_TIMEOUT,
msg.header.msgh_size,
0,
MACH_PORT_NULL,
MACH_MSG_TIMEOUT_NONE,
MACH_PORT_NULL);
if (status == MACH_SEND_TIMED_OUT) {
mach_msg_destroy(&msg.header);
}
if (status != MACH_MSG_SUCCESS)
{
}
return;
}
static void describeWakeEvent(
PMConnection *inConnection,
CFMutableStringRef allWakeEvents,
CFStringRef describeType,
CFAbsoluteTime requestedTime,
CFStringRef clientInfoString)
{
CFStringRef descriptionString = NULL;
char name[32];
pid_t use_pid = 0;
if (!allWakeEvents)
return;
use_pid = inConnection ? inConnection->callerPID : 0;
if (0 == use_pid) {
use_pid = getpid();
}
if ((0 == use_pid)
|| !proc_name(use_pid, name, sizeof(name))) {
name[0] = '\0';
}
descriptionString = CFStringCreateWithFormat(0, 0, CFSTR("[proc=%s request=%@ inDelta=%.0f%@%@] "),
name, describeType,
(requestedTime - CFAbsoluteTimeGetCurrent()),
clientInfoString ? CFSTR(" info="):CFSTR(""),
clientInfoString ? clientInfoString:CFSTR(""));
if (descriptionString) {
CFStringAppend(allWakeEvents, descriptionString);
CFRelease(descriptionString);
}
}
#pragma mark -
#pragma mark CheckResponses
static bool checkResponses_ScheduleWakeEvents(PMResponseWrangler *wrangler)
{
int i = 0;
int responsesCount = 0;
bool complete = true;
PMResponse *oneResponse = NULL;
CFMutableStringRef allWakeEventsString = NULL;
CFAbsoluteTime pick[kChooseWakeTypeCount];
bzero(pick, sizeof(pick));
if (PMDebugEnabled(kLogWakeEvents)) {
allWakeEventsString = CFStringCreateMutable(0, 0);
}
if (_DWBT_allowed() && gWakeForDWBTInterval) {
describeWakeEvent(NULL,
allWakeEventsString, CFSTR("BTIntervalWaitToDarkWake"),
gWakeForDWBTInterval , NULL);
}
responsesCount = CFArrayGetCount(wrangler->awaitingResponses);
for (i=0; i<responsesCount; i++)
{
oneResponse = (PMResponse *)CFArrayGetValueAtIndex(wrangler->awaitingResponses, i);
if (!oneResponse->replied) {
complete = false;
break;
}
if (VALID_DATE(oneResponse->maintenanceRequested))
{
describeWakeEvent(oneResponse->connection,
allWakeEventsString, CFSTR("Maintenance"),
oneResponse->maintenanceRequested,
oneResponse->clientInfoString);
}
if (VALID_DATE(oneResponse->sleepServiceRequested))
{
describeWakeEvent(oneResponse->connection,
allWakeEventsString, CFSTR("SleepService"),
oneResponse->sleepServiceRequested,
oneResponse->clientInfoString);
}
if (VALID_DATE(oneResponse->timerPluginRequested))
{
describeWakeEvent(oneResponse->connection,
allWakeEventsString, CFSTR("TimerPlugin"),
oneResponse->timerPluginRequested,
oneResponse->clientInfoString);
}
if (!VALID_DATE(pick[kChooseMaintenance])
|| (VALID_DATE(oneResponse->maintenanceRequested) && (oneResponse->maintenanceRequested < pick[kChooseMaintenance])))
{
pick[kChooseMaintenance] = oneResponse->maintenanceRequested;
}
if (!VALID_DATE(pick[kChooseSleepServiceWake])
|| (VALID_DATE(oneResponse->sleepServiceRequested) && (oneResponse->sleepServiceRequested < pick[kChooseSleepServiceWake])))
{
pick[kChooseSleepServiceWake] = oneResponse->sleepServiceRequested;
}
if (!VALID_DATE(pick[kChooseTimerPlugin])
|| (VALID_DATE(oneResponse->timerPluginRequested) && (oneResponse->timerPluginRequested < pick[kChooseTimerPlugin])))
{
pick[kChooseTimerPlugin] = oneResponse->timerPluginRequested;
}
}
if (!complete) {
goto exit;
}
if (!BIT_IS_SET(wrangler->notificationType, kIOPMSystemCapabilityCPU))
{
logASLMessagePMConnectionScheduledWakeEvents(allWakeEventsString);
PMScheduleWakeEventChooseBest(pick);
}
exit:
if (allWakeEventsString){
CFRelease(allWakeEventsString);
}
return complete;
}
static void checkResponses(PMResponseWrangler *wrangler)
{
if (!checkResponses_ScheduleWakeEvents(wrangler)) {
return;
}
if (wrangler->awaitingResponsesTimeout) {
CFRunLoopTimerInvalidate(wrangler->awaitingResponsesTimeout);
wrangler->awaitingResponsesTimeout = NULL;
}
if (wrangler->kernelAcknowledgementID)
{
IOAllowPowerChange(gRootDomainConnect, wrangler->kernelAcknowledgementID);
}
cleanupResponseWrangler(wrangler);
return;
}
#define IS_EARLIEST_EVENT(x, y, z) (VALID_DATE(x) && ((y==0.0)||(x<y)) && ((z==0.0)||(x<z)))
static bool pickEarliestEvent(CFAbsoluteTime *inArray, int *outIndex, CFAbsoluteTime *outTime)
{
int i = 0;
CFAbsoluteTime lowest = kCFAbsoluteTimeIntervalSince1904;
if (outIndex) *outIndex = 0;
if (outTime) *outTime = 0.0;
if (!inArray || !outIndex || !outTime)
return false;
for (i=0; i<kChooseWakeTypeCount; i++)
{
if ((0.0 != inArray[i]) && (inArray[i] < lowest)) {
*outIndex = i;
*outTime = lowest = inArray[i];
}
}
return (0.0 != *outTime);
}
__private_extern__ void PMScheduleWakeEventChooseBest(CFAbsoluteTime *pickWakeEvent)
{
CFStringRef scheduleWakeType = NULL;
CFAbsoluteTime scheduleTime = 0.0;
CFAbsoluteTime dummy[kChooseWakeTypeCount];
int idx;
if (!pickWakeEvent) {
bzero(dummy, sizeof(dummy));
pickWakeEvent = dummy;
}
pickWakeEvent[kChooseFullWake] = getEarliestRequestAutoWake();
if (_DWBT_allowed() && gWakeForDWBTInterval) {
pickWakeEvent[kChooseDWBTInterval] = gWakeForDWBTInterval;
}
if (!pickEarliestEvent(pickWakeEvent, &idx, &scheduleTime))
{
return;
}
if ((kChooseMaintenance == idx) || (kChooseDWBTInterval == idx)
|| (kChooseTimerPlugin == idx))
{
scheduleWakeType = CFSTR(kIOPMMaintenanceScheduleImmediate);
} else if (kChooseFullWake == idx)
{
scheduleWakeType = CFSTR(kIOPMAutoWakeScheduleImmediate);
} else if (kChooseSleepServiceWake == idx)
{
scheduleWakeType = CFSTR(kIOPMSleepServiceScheduleImmediate);
}
if (VALID_DATE(scheduleTime) && scheduleWakeType)
{
CFDateRef theChosenDate = NULL;
if ((theChosenDate = CFDateCreate(0, scheduleTime)))
{
IOPMSchedulePowerEvent(theChosenDate, NULL, scheduleWakeType);
CFRelease(theChosenDate);
}
if (PMDebugEnabled(kLogWakeEvents))
{
CFMutableStringRef finalPublish = CFStringCreateMutable(0, 0);
if (finalPublish)
{
CFStringAppend(finalPublish, scheduleWakeType);
CFStringAppendFormat(finalPublish, NULL, CFSTR(" inDelta=%.2lf"), (scheduleTime - CFAbsoluteTimeGetCurrent()));
logASLMessageExecutedWakeupEvent(finalPublish);
CFRelease(finalPublish);
}
}
}
return ;
}
static CFArrayRef createArrayOfConnectionsWithInterest(
int interestBits)
{
CFMutableArrayRef arrayFoundInterests = NULL;
PMConnection *lookee;
int gConnectionsCount;
int i;
if (0 == interestBits)
return NULL;
arrayFoundInterests = CFArrayCreateMutable(kCFAllocatorDefault, 0, &_CFArrayConnectionCallBacks);
gConnectionsCount = CFArrayGetCount(gConnections);
for (i=0; i<gConnectionsCount; i++)
{
lookee = (PMConnection *)CFArrayGetValueAtIndex(gConnections, i);
if (interestBits & lookee->interestsBits) {
CFArrayAppendValue(arrayFoundInterests, lookee);
}
}
return arrayFoundInterests;
}
static IOReturn createConnectionWithID(PMConnection **out)
{
static bool hasLoggedTooManyConnections = false;
if ((globalConnectionIDTally > kMaxConnectionIDCount)
&& !hasLoggedTooManyConnections)
{
return kIOReturnNoSpace;
}
*out = (PMConnection *)calloc(1, sizeof(PMConnection));
if (!*out)
return kIOReturnNoMemory;
((PMConnection *)*out)->uniqueID = kConnectionOffset + globalConnectionIDTally++;
CFArrayAppendValue(gConnections, *out);
return kIOReturnSuccess;
}
static PMConnection *connectionForID(uint32_t findMe)
{
CFIndex where = 0;
CFRange theRange = CFRangeMake(0, CFArrayGetCount(gConnections));
PMConnection dummy;
dummy.uniqueID = findMe;
where = CFArrayGetFirstIndexOfValue(gConnections, theRange, &dummy);
if (kCFNotFound == where) {
return NULL;
} else {
return (PMConnection *)CFArrayGetValueAtIndex(gConnections, where);
}
}
__private_extern__ IOReturn _unclamp_silent_running(void)
{
if(gCurrentSilentRunningState == kSilentRunningOff)
return kIOReturnSuccess;
cancelPowerNapStates();
if(!smcSilentRunningSupport())
return kIOReturnUnsupported;
int newSRCap = kSilentRunningOff;
CFNumberRef num = CFNumberCreate(0, kCFNumberIntType, &newSRCap);
if (num)
{
if(rootDomainService)
{
IORegistryEntrySetCFProperty(rootDomainService,
CFSTR(kIOPMSilentRunningKey),
num);
gCurrentSilentRunningState = kSilentRunningOff;
}
else
{
CFRelease(num);
return kIOReturnInternalError;
}
CFRelease(num);
return kIOReturnSuccess;
}
else
{
return kIOReturnInternalError;
}
}