#include <IOKit/pwr_mgt/IOPMLibPrivate.h>
#include <unistd.h>
#include <stdlib.h>
#include <notify.h>
#include <asl.h>
#include <mach/mach.h>
#include <mach/mach_host.h>
#include <mach/mach_error.h>
#include <servers/bootstrap.h>
#include <dispatch/dispatch.h>
#include <bsm/libbsm.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <libproc.h>
#include "PMConnection.h"
#include "PrivateLib.h"
#include "PMSettings.h"
#include "PMAssertions.h"
#include "BatteryTimeRemaining.h"
#include "PMStore.h"
#include "powermanagementServer.h"
#define kIOPMAppName "Power Management configd plugin"
#define kIOPMPrefsPath "com.apple.PowerManagement.xml"
#define FMMD_WIPE_BOOT_ARG "fmm-wipe-system-status"
#define kMaxAssertions 10240
#define kMaxTaskAssertions 1024
#define kIOPMTaskPortKey CFSTR("task")
#define kIOPMTaskPIDKey CFSTR("pid")
#define kIOPMTaskAssertionsKey CFSTR("assertions")
#define kIOPMTaskDispatchSourceKey CFSTR("dispatchsource")
#define kIOPMTaskNameKey CFSTR("name")
#define CAST_PID_TO_KEY(x) ((void *)(uintptr_t)(x))
#define kIOPMAssertionTimerRefKey CFSTR("AssertTimerRef")
#define kIOPMAssertionLevelsBits CFSTR("LevelsBitfield")
enum {
kSBUCInflowDisable = 0,
kSBUCChargeInhibit = 1
};
typedef enum {
kTimerTypeTimedOut = 0,
kTimerTypeReleased = 1
} TimerType;
#define ID_FROM_INDEX(idx) (idx + 300)
#define INDEX_FROM_ID(id) (id - 300)
#define MAKE_UNIQAID(pid, assertId, id_cnt) ((uint64_t)(pid) << 32) | ((assertId) & 0xffff) << 16 | ((id_cnt) & 0xffff)
#define GET_ASSERTID(uniqaid) (((uniqaid) >> 16) & 0xffff)
#define GET_ASSERTPID(uniqaid) (((uniqaid) >> 32) & 0xffffffff)
#define SET_AGGREGATE_LEVEL(idx, val) { if (val) aggregate_assertions |= (1 << idx); \
else aggregate_assertions &= ~(1<<idx); }
#define LEVEL_FOR_BIT(idx) ((aggregate_assertions & (1 << idx)) ? 1:0)
#define LAST_LEVEL_FOR_BIT(idx) ((last_aggregate_assertions & (1 << idx)) ? 1:0)
#define ASSERTION_LOG_DELAY (5LL)
CFArrayRef copyScheduledPowerEvents(void);
CFDictionaryRef copyRepeatPowerEvents(void);
static void sendSmartBatteryCommand(uint32_t which, uint32_t level);
static void sendUserAssertionsToKernel(uint32_t user_assertions);
static void evaluateAssertions(void);
static void HandleProcessExit(pid_t deadPID);
#if !TARGET_OS_EMBEDDED
static void logASLAssertionEvent(
const char *assertionAction,
assertion_t *assertion);
#else
#define logASLAssertionEvent(X1, X2)
#define logASLAssertionSummary()
#define logASLAssertionsAggregate()
#endif
static bool propertiesDictRequiresRoot(CFDictionaryRef props);
static IOReturn doRetain(pid_t pid, IOPMAssertionID id);
static IOReturn doRelease(pid_t pid, IOPMAssertionID id);
static IOReturn doSetProperties(pid_t pid,
IOPMAssertionID id,
CFDictionaryRef props);
static CFArrayRef copyPIDAssertionDictionaryFlattened(void);
static CFDictionaryRef copyAggregateValuesDictionary(void);
static CFArrayRef copyTimedOutAssertionsArray(void);
static IOReturn doCreate(pid_t pid, CFMutableDictionaryRef newProperties,
IOPMAssertionID *assertion_id);
static IOReturn copyAssertionForID(
pid_t inPID, int inID,
CFMutableDictionaryRef *outAssertion);
static void processInfoCreate(pid_t p, dispatch_source_t d);
static bool processInfoRetain(pid_t p);
static void processInfoRelease(pid_t p);
static void sendActivityTickle ();
static const int kMaxCountTimedOut = 5;
extern CFMachPortRef pmServerMachPort;
static CFMutableArrayRef gTimedOutArray = NULL;
static uint32_t kerAssertionBits = 0;
static int aggregate_assertions;
static CFStringRef assertion_types_arr[kIOPMNumAssertionTypes];
__private_extern__ bool isDisplayAsleep( );
__private_extern__ void logASLMessageSleepServiceTerminated(int forcedTimeoutCnt);
static CFMutableDictionaryRef gAssertionsArray = NULL;
static CFMutableDictionaryRef gProcessDict = NULL;
static CFMutableDictionaryRef gUserAssertionTypesDict = NULL;
assertionType_t gAssertionTypes[kIOPMNumAssertionTypes];
uint32_t gDisplaySleepTimer = 0;
void handleAssertionTimeout(assertionType_t *assertType);
void resetGlobalTimer(assertionType_t *assertType, uint64_t timer);
static IOReturn raiseAssertion(assertion_t *assertion);
dispatch_source_t logDispatch = NULL;
static uint32_t gNextAssertionIdx = 0;
extern uint32_t gDebugFlags;
#define kPMMaxDisplayTurnOffDelay (5)
#pragma mark -
#pragma mark MIG
kern_return_t _io_pm_assertion_create
(
mach_port_t server __unused,
audit_token_t token,
vm_offset_t props,
mach_msg_type_number_t propsCnt,
int *assertion_id,
int *return_code
)
{
CFMutableDictionaryRef newAssertionProperties = NULL;
CFDataRef unfolder = NULL;
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);
unfolder = CFDataCreateWithBytesNoCopy(0, (const UInt8 *)props, propsCnt, kCFAllocatorNull);
if (unfolder) {
newAssertionProperties = (CFMutableDictionaryRef)CFPropertyListCreateWithData(
0, unfolder, kCFPropertyListMutableContainersAndLeaves, NULL, NULL);
CFRelease(unfolder);
}
if (!newAssertionProperties) {
*return_code = kIOReturnBadArgument;
goto exit;
}
if (propertiesDictRequiresRoot(newAssertionProperties)
&& ( !(callerIsRoot(callerUID) || callerIsAdmin(callerUID, callerGID))))
{
*return_code = kIOReturnNotPrivileged;
goto exit;
}
*return_code = doCreate(callerPID, newAssertionProperties, (IOPMAssertionID *)assertion_id);
exit:
if (newAssertionProperties) {
CFRelease(newAssertionProperties);
}
vm_deallocate(mach_task_self(), props, propsCnt);
return KERN_SUCCESS;
}
kern_return_t _io_pm_assertion_set_properties
(
mach_port_t server __unused,
audit_token_t token,
int assertion_id,
vm_offset_t props,
mach_msg_type_number_t propsCnt,
int *return_code
)
{
CFDictionaryRef setProperties = NULL;
CFDataRef unfolder = NULL;
pid_t callerPID = -1;
audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, &callerPID, NULL, NULL);
unfolder = CFDataCreateWithBytesNoCopy(0, (const UInt8 *)props, propsCnt, kCFAllocatorNull);
if (unfolder) {
setProperties = (CFDictionaryRef)CFPropertyListCreateWithData(0, unfolder, 0, NULL, NULL);
CFRelease(unfolder);
}
if (!setProperties) {
*return_code = kIOReturnBadArgument;
goto exit;
}
*return_code = doSetProperties(callerPID, assertion_id, setProperties);
CFRelease(setProperties);
exit:
vm_deallocate(mach_task_self(), props, propsCnt);
return KERN_SUCCESS;
}
kern_return_t _io_pm_assertion_retain_release
(
mach_port_t server __unused,
audit_token_t token,
int assertion_id,
int action,
int *return_code
)
{
pid_t callerPID = -1;
audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, &callerPID, NULL, NULL);
if (kIOPMAssertionMIGDoRetain == action) {
*return_code = doRetain(callerPID, assertion_id);
} else {
*return_code = doRelease(callerPID, assertion_id);
}
return KERN_SUCCESS;
}
kern_return_t _io_pm_assertion_copy_details
(
mach_port_t server,
audit_token_t token,
int assertion_id,
int whichData,
vm_offset_t *assertions,
mach_msg_type_number_t *assertionsCnt,
int *return_val
)
{
CFTypeRef theCollection = NULL;
CFDataRef serializedDetails = NULL;
pid_t callerPID = -1;
*return_val = kIOReturnNotFound;
if (kIOPMAssertionMIGCopyAll == whichData)
{
theCollection = copyPIDAssertionDictionaryFlattened();
} else if (kIOPMAssertionMIGCopyOneAssertionProperties == whichData)
{
audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, &callerPID, NULL, NULL);
*return_val = copyAssertionForID(callerPID, assertion_id,
(CFMutableDictionaryRef *)&theCollection);
} else if (kIOPMAssertionMIGCopyStatus == whichData)
{
theCollection = copyAggregateValuesDictionary();
} else if (kIOPMAssertionMIGCopyTimedOutAssertions == whichData)
{
theCollection = copyTimedOutAssertionsArray();
}
else if (kIOPMPowerEventsMIGCopyScheduledEvents == whichData)
{
theCollection = copyScheduledPowerEvents();
}
else if (kIOPMPowerEventsMIGCopyRepeatEvents == whichData)
{
theCollection = copyRepeatPowerEvents();
}
if (!theCollection) {
*assertionsCnt = 0;
*assertions = 0;
*return_val = kIOReturnSuccess;
return KERN_SUCCESS;
}
serializedDetails = CFPropertyListCreateData(0, theCollection,
kCFPropertyListBinaryFormat_v1_0, 0, NULL);
CFRelease(theCollection);
if (serializedDetails)
{
*assertionsCnt = CFDataGetLength(serializedDetails);
vm_allocate(mach_task_self(), (vm_address_t *)assertions, *assertionsCnt, TRUE);
memcpy((void *)*assertions, CFDataGetBytePtr(serializedDetails), *assertionsCnt);
CFRelease(serializedDetails);
*return_val = kIOReturnSuccess;
} else {
*return_val = kIOReturnInternalError;
}
return KERN_SUCCESS;
}
static void sendActivityTickle ()
{
io_service_t rootDomainService = IO_OBJECT_NULL;
io_connect_t gRootDomainConnect = IO_OBJECT_NULL;
kern_return_t kr = 0;
IOReturn ret;
if (!isDisplayAsleep( ))
return;
rootDomainService = getRootDomain();
if (IO_OBJECT_NULL == rootDomainService) {
goto exit;
}
kr = IOServiceOpen(rootDomainService, mach_task_self(), 0, &gRootDomainConnect);
if (KERN_SUCCESS != kr) {
goto exit;
}
ret = IOConnectCallMethod(gRootDomainConnect, kPMActivityTickle,
NULL, 0,
NULL, 0, NULL,
NULL, NULL, NULL);
if (kIOReturnSuccess != ret)
{
goto exit;
}
exit:
if (IO_OBJECT_NULL != gRootDomainConnect)
IOServiceClose(gRootDomainConnect);
return;
}
static void sendUserAssertionsToKernel(uint32_t user_assertions)
{
io_service_t rootDomainService = IO_OBJECT_NULL;
io_connect_t gRootDomainConnect = IO_OBJECT_NULL;
kern_return_t kr = 0;
const uint64_t in = (uint64_t)user_assertions;
IOReturn ret;
rootDomainService = getRootDomain();
if (IO_OBJECT_NULL == rootDomainService) {
goto exit;
}
kr = IOServiceOpen(rootDomainService, mach_task_self(), 0, &gRootDomainConnect);
if (KERN_SUCCESS != kr) {
goto exit;
}
ret = IOConnectCallMethod(gRootDomainConnect, kPMSetUserAssertionLevels,
&in, 1,
NULL, 0, NULL,
NULL, NULL, NULL);
if (kIOReturnSuccess != ret)
{
goto exit;
}
exit:
if (IO_OBJECT_NULL != gRootDomainConnect)
IOServiceClose(gRootDomainConnect);
return;
}
#pragma mark -
#pragma mark Act on assertions
__private_extern__ void PMAssertions_SettingsHaveChanged(void)
{
static int lastDWBTSetting = -1;
int newDWBT = GetPMSettingBool(CFSTR(kIOPMDarkWakeBackgroundTaskKey));
if (newDWBT == lastDWBTSetting) {
return;
}
lastDWBTSetting = newDWBT;
configAssertionType(kBackgroundTaskIndex, false);
}
#if HAVE_SMART_BATTERY
static void
sendSmartBatteryCommand(uint32_t which, uint32_t level)
{
io_service_t sbmanager = MACH_PORT_NULL;
io_connect_t sbconnection = MACH_PORT_NULL;
kern_return_t kret;
uint32_t output_count = 1;
uint64_t uc_return = kIOReturnError;
uint64_t level_64 = level;
sbmanager = IOServiceGetMatchingService(MACH_PORT_NULL,
IOServiceMatching("AppleSmartBatteryManager"));
if (MACH_PORT_NULL == sbmanager) {
goto bail;
}
kret = IOServiceOpen( sbmanager, mach_task_self(), 0, &sbconnection);
if (kIOReturnSuccess != kret) {
goto bail;
}
IOConnectCallMethod(
sbconnection, which, &level_64, 1, NULL, 0, &uc_return, &output_count, NULL, 0);
bail:
if (MACH_PORT_NULL != sbconnection) {
IOServiceClose(sbconnection);
}
if (MACH_PORT_NULL != sbmanager) {
IOObjectRelease(sbmanager);
}
return;
}
#else
static void
sendSmartBatteryCommand(uint32_t which, uint32_t level)
{
kern_return_t kr;
io_iterator_t iter;
io_registry_entry_t next;
do
{
kr = IOServiceGetMatchingServices(kIOMasterPortDefault,
IOServiceMatching("IOPMPowerSource"), &iter);
if (kIOReturnSuccess != kr)
break;
if (MACH_PORT_NULL == iter)
break;
while ((next = IOIteratorNext(iter)))
{
kr = IORegistryEntrySetCFProperty(next,
(which == kSBUCChargeInhibit) ? CFSTR(kIOPMPSIsChargingKey) : CFSTR(kIOPMPSExternalConnectedKey),
level ? kCFBooleanFalse : kCFBooleanTrue);
IOObjectRelease(next);
}
IOObjectRelease(iter);
}
while (false);
return;
}
#endif
__private_extern__ IOReturn
_IOPMSetActivePowerProfilesRequiresRoot
(
CFDictionaryRef which_profile,
int uid,
int gid
)
{
IOReturn ret = kIOReturnError;
SCPreferencesRef energyPrefs = NULL;
if ( (!callerIsRoot(uid) &&
!callerIsAdmin(uid, gid) &&
!callerIsConsole(uid, gid)) ||
( (-1 == uid) || (-1 == gid) ))
{
ret = kIOReturnNotPrivileged;
goto exit;
}
if (!which_profile) {
ret = kIOReturnBadArgument;
goto exit;
}
energyPrefs = SCPreferencesCreate( kCFAllocatorDefault, CFSTR(kIOPMAppName), CFSTR(kIOPMPrefsPath) );
if (!energyPrefs) {
goto exit;
}
if (!SCPreferencesLock(energyPrefs, true))
{
ret = kIOReturnInternalError;
goto exit;
}
if (!SCPreferencesSetValue(energyPrefs, CFSTR("ActivePowerProfiles"), which_profile)) {
goto exit;
}
if (!SCPreferencesCommitChanges(energyPrefs))
{
if (kSCStatusAccessError == SCError()) ret = kIOReturnNotPrivileged;
else ret = kIOReturnError;
goto exit;
}
if (!SCPreferencesApplyChanges(energyPrefs))
{
if (kSCStatusAccessError == SCError()) ret = kIOReturnNotPrivileged;
else ret = kIOReturnError;
goto exit;
}
ret = kIOReturnSuccess;
exit:
if (energyPrefs) {
SCPreferencesUnlock(energyPrefs);
CFRelease(energyPrefs);
}
return ret;
}
void createOnBootAssertions( )
{
CFMutableDictionaryRef assertionDescription = NULL;
int value;
IOReturn ret;
ret = getNvramArgInt(FMMD_WIPE_BOOT_ARG, &value);
if (ret == kIOReturnSuccess && value > 0) {
assertionDescription = _IOPMAssertionDescriptionCreate(
kIOPMAssertionTypePreventSystemSleep,
CFSTR("com.apple.powermanagement.fmmdwipe"),
NULL, CFSTR("Proxy Assertion during FMMD system wipe"), NULL,
120, kIOPMAssertionTimeoutActionRelease);
CFDictionarySetValue(assertionDescription,
kIOPMAssertionAppliesToLimitedPowerKey, (CFBooleanRef)kCFBooleanTrue);
InternalCreateAssertion(assertionDescription, NULL);
CFRelease(assertionDescription);
}
}
static CFDictionaryRef copyAggregateValuesDictionary(void)
{
CFDictionaryRef assertions_info = NULL;
CFNumberRef cf_agg_vals[kIOPMNumAssertionTypes];
int i;
for (i=0; i<kIOPMNumAssertionTypes; i++)
{
int tmp_bit = LEVEL_FOR_BIT(i);
cf_agg_vals[i] = CFNumberCreate(0, kCFNumberIntType, &tmp_bit);
}
assertions_info = CFDictionaryCreate(
0,
(const void **)assertion_types_arr, (const void **)cf_agg_vals, kIOPMNumAssertionTypes,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
for (i=0; i<kIOPMNumAssertionTypes; i++)
{
CFRelease(cf_agg_vals[i]);
}
return assertions_info;
}
static CFArrayRef copyTimedOutAssertionsArray(void)
{
if (gTimedOutArray)
{
int arrayCount;
int i;
CFMutableArrayRef newTimeOutArray;
newTimeOutArray = CFArrayCreateMutableCopy(0, 0, gTimedOutArray);
arrayCount = CFArrayGetCount(newTimeOutArray);
for (i = 0; i<arrayCount; i++)
{
CFMutableDictionaryRef assertionCopy =
CFDictionaryCreateMutableCopy(0, 0, CFArrayGetValueAtIndex(newTimeOutArray, i));
CFDictionaryRemoveValue(assertionCopy, kIOPMAssertionTimerRefKey);
CFArraySetValueAtIndex(newTimeOutArray, i, assertionCopy);
CFRelease(assertionCopy);
}
return newTimeOutArray;
}
return NULL;
}
static void appendTimedOutAssertion(CFDictionaryRef timedout)
{
int timedoutcount = 0;
if (!gTimedOutArray) {
gTimedOutArray = CFArrayCreateMutable(0, kMaxCountTimedOut, &kCFTypeArrayCallBacks);
}
if (!gTimedOutArray)
return;
timedoutcount = CFArrayGetCount(gTimedOutArray);
if (kMaxCountTimedOut == timedoutcount) {
CFArrayRemoveValueAtIndex(gTimedOutArray, kMaxCountTimedOut - 1);
}
CFArrayInsertValueAtIndex(gTimedOutArray, 0, timedout);
}
__private_extern__ void _PMAssertionsDriverAssertionsHaveChanged(uint32_t changedDriverAssertions)
{
notify_post( kIOPMAssertionsChangedNotifyString );
}
#pragma mark -
#pragma mark Retain & Release
#pragma mark -
#pragma mark ASL Log
#if !TARGET_OS_EMBEDDED
__private_extern__ void logASLAssertionTypeSummary( kerAssertionType type)
{
applyToAllAssertionsSync(&gAssertionTypes[type], false, ^(assertion_t *assertion)
{
logASLAssertionEvent(kPMASLAssertionActionSummary, assertion);
});
}
static void logASLAssertionEvent(
const char *assertionAction,
assertion_t *assertion)
{
const int kLongStringLen = 200;
const int kShortStringLen = 10;
aslmsg m;
CFStringRef foundAssertionType = NULL;
CFStringRef foundAssertionName = NULL;
CFDateRef foundDate = NULL;
CFStringRef procName = NULL;
char proc_name_buf[kProcNameBufLen];
char pid_buf[kShortStringLen];
char assertionTypeCString[kLongStringLen];
char assertionNameCString[kLongStringLen];
char ageString[kShortStringLen];
char aslMessageString[kLongStringLen];
CFMutableDictionaryRef assertionDictionary;
if ((gDebugFlags & kIOPMDebugEnableAssertionLogging) == 0)
return;
if (assertionAction == kPMASLAssertionActionCreate) {
if (assertion->state & kAssertionStateLogged)
return;
assertion->state |= kAssertionStateLogged;
}
else if ( (assertion->state & kAssertionStateLogged) == 0) {
return;
}
m = asl_new(ASL_TYPE_MSG);
asl_set(m, kMsgTracerDomainKey, kMsgTracerDomainPMAssertions);
pid_buf[0] = 0;
if (0 < snprintf(pid_buf, kShortStringLen, "%d", assertion->pid)) {
asl_set(m, kPMASLPIDKey, pid_buf);
}
asl_set(m, kPMASLActionKey, assertionAction);
assertionDictionary = assertion->props;
if (assertionDictionary)
{
foundAssertionType = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionTypeKey);
if (foundAssertionType) {
CFStringGetCString(foundAssertionType, assertionTypeCString, sizeof(assertionTypeCString), kCFStringEncodingUTF8);
asl_set(m, kPMASLAssertionNameKey, assertionTypeCString);
}
foundAssertionName = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionNameKey);
if (foundAssertionName) {
CFStringGetCString(foundAssertionName, assertionNameCString, sizeof(assertionNameCString), kCFStringEncodingUTF8);
}
if ((foundDate = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionCreateDateKey)))
{
CFAbsoluteTime createdCFTime = CFDateGetAbsoluteTime(foundDate);
int createdSince = (int)(CFAbsoluteTimeGetCurrent() - createdCFTime);
int hours = createdSince / 3600;
int minutes = (createdSince / 60) % 60;
int seconds = createdSince % 60;
snprintf(ageString, sizeof(ageString), "%02d:%02d:%02d ", hours, minutes, seconds);
}
int retainCount = assertion->retainCnt;
if (1 != retainCount)
{
char retainCountBuf[kShortStringLen];
snprintf(retainCountBuf, sizeof(retainCountBuf), "%d", retainCount);
asl_set(m, "RetainCount", retainCountBuf);
}
}
if ((procName = processInfoGetName(assertion->pid)))
{
CFStringGetCString(procName, proc_name_buf, sizeof(proc_name_buf), kCFStringEncodingUTF8);
}
snprintf(aslMessageString, sizeof(aslMessageString), "PID %s(%s) %s %s %s%s%s %s id:0x%llx Aggregate:0x%x",
pid_buf,
procName ? proc_name_buf:"?",
assertionAction,
foundAssertionType ? assertionTypeCString:"",
foundAssertionName?"\"":"", foundAssertionName ? assertionNameCString:"",
foundAssertionName?"\"":"",
foundDate?ageString:"",
(((uint64_t)assertion->kassert) << 32) | (assertion->assertionId),
((kerAssertionBits & 0xfff) << 16) | (aggregate_assertions & 0xffff));
asl_set(m, ASL_KEY_MSG, aslMessageString);
asl_set(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
asl_set(m, ASL_KEY_FACILITY, "internal");
asl_set(m, kPMASLMessageKey, kPMASLMessageLogValue);
asl_send(NULL, m);
asl_free(m);
}
static void
logASLAssertionsAggregate( )
{
aslmsg m;
char aslMessageString[100];
m = asl_new(ASL_TYPE_MSG);
asl_set(m, kMsgTracerDomainKey, kMsgTracerDomainPMAssertions);
asl_set(m, kPMASLActionKey, kPMASLAssertionActionSummary);
snprintf(aslMessageString, sizeof(aslMessageString), "Summary- Aggregate:0x%x Using %s",
((kerAssertionBits & 0xfff) << 16) | (aggregate_assertions & 0xffff),
( _getPowerSource() == kBatteryPowered) ? "Batt" : "AC");
asl_set(m, ASL_KEY_MSG, aslMessageString);
asl_set(m, ASL_KEY_LEVEL, ASL_STRING_NOTICE);
asl_set(m, ASL_KEY_FACILITY, "internal");
asl_set(m, kPMASLMessageKey, kPMASLMessageLogValue);
asl_send(NULL, m);
asl_free(m);
}
#endif
#define kDarkWakeNetworkHoldForSeconds 30
#define kDeviceEnumerationHoldForSeconds (45LL)
#pragma mark -
#pragma mark powerd-Internal Use Only
__private_extern__ CFMutableDictionaryRef _IOPMAssertionDescriptionCreate(
CFStringRef AssertionType,
CFStringRef Name,
CFStringRef Details,
CFStringRef HumanReadableReason,
CFStringRef LocalizationBundlePath,
CFTimeInterval Timeout,
CFStringRef TimeoutBehavior)
{
CFMutableDictionaryRef descriptor = NULL;
if (!AssertionType || !Name) {
return NULL;
}
descriptor = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
if (!descriptor) {
return NULL;
}
CFDictionarySetValue(descriptor, kIOPMAssertionNameKey, Name);
int _on = kIOPMAssertionLevelOn;
CFNumberRef _on_num = CFNumberCreate(0, kCFNumberIntType, &_on);
CFDictionarySetValue(descriptor, kIOPMAssertionLevelKey, _on_num);
CFRelease(_on_num);
CFDictionarySetValue(descriptor, kIOPMAssertionTypeKey, AssertionType);
if (Details) {
CFDictionarySetValue(descriptor, kIOPMAssertionDetailsKey, Details);
}
if (HumanReadableReason) {
CFDictionarySetValue(descriptor, kIOPMAssertionHumanReadableReasonKey, HumanReadableReason);
}
if (LocalizationBundlePath) {
CFDictionarySetValue(descriptor, kIOPMAssertionLocalizationBundlePathKey, LocalizationBundlePath);
}
if (Timeout) {
CFNumberRef Timeout_num = CFNumberCreate(0, kCFNumberDoubleType, &Timeout);
CFDictionarySetValue(descriptor, kIOPMAssertionTimeoutKey, Timeout_num);
CFRelease(Timeout_num);
}
if (TimeoutBehavior)
{
CFDictionarySetValue(descriptor, kIOPMAssertionTimeoutActionKey, TimeoutBehavior);
}
return descriptor;
}
__private_extern__ IOReturn InternalCreateAssertion(
CFMutableDictionaryRef properties,
IOPMAssertionID *outID)
{
if (!properties)
return kIOReturnBadArgument;
CFRetain(properties);
CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, ^{
if (outID == NULL) {
IOPMAssertionID assertionID = kIOPMNullAssertionID;
doCreate(getpid(), properties, &assertionID);
}
else if ( *outID == kIOPMNullAssertionID )
doCreate(getpid(), properties, outID);
CFRelease(properties);
});
CFRunLoopWakeUp(_getPMRunLoop());
return kIOReturnSuccess;
}
__private_extern__ void InternalReleaseAssertion(
IOPMAssertionID *outID)
{
CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, ^{
if ( *outID != kIOPMNullAssertionID ) {
doRelease(getpid(), *outID);
}
*outID = kIOPMNullAssertionID;
});
CFRunLoopWakeUp(_getPMRunLoop());
}
__private_extern__ void InternalEvaluateAssertions(void)
{
CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, ^{
evaluateAssertions();
});
CFRunLoopWakeUp(_getPMRunLoop());
}
static IOReturn _localCreateAssertionWithTimer(
CFStringRef type, IOPMAssertionLevel level, CFStringRef name, int timerSecs, IOPMAssertionID *outID)
{
CFMutableDictionaryRef dict = NULL;
CFNumberRef levelNum = NULL;
CFNumberRef timerNum = NULL;
if (!type || !name || !outID) {
return kIOReturnBadArgument;
}
if ((dict = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
{
if ((levelNum = CFNumberCreate(0, kCFNumberIntType, &level)))
{
CFDictionarySetValue(dict, kIOPMAssertionLevelKey, levelNum);
CFRelease(levelNum);
}
if (timerSecs && (timerNum = CFNumberCreate(0, kCFNumberIntType, &timerSecs)) )
{
CFDictionarySetValue(dict, kIOPMAssertionTimeoutKey, timerNum);
CFRelease(timerNum);
CFDictionarySetValue(dict, kIOPMAssertionTimeoutActionKey, kIOPMAssertionTimeoutActionRelease);
}
CFDictionarySetValue(dict, kIOPMAssertionTypeKey, type);
CFDictionarySetValue(dict, kIOPMAssertionNameKey, name);
doCreate(getpid(), dict, outID);
CFRelease(dict);
}
return kIOReturnSuccess;
}
static IOReturn _localCreateAssertion(CFStringRef type, IOPMAssertionLevel level, CFStringRef name, IOPMAssertionID *outID)
{
return _localCreateAssertionWithTimer(type, level, name, 0, outID);
}
static IOReturn _enableAssertionForLimitedPower(pid_t pid, IOPMAssertionID id)
{
CFMutableDictionaryRef dict = NULL;
IOReturn rc = kIOReturnError;
if (!id)
return kIOReturnBadArgument;
if ((dict = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks)))
{
CFDictionarySetValue(dict, kIOPMAssertionAppliesToLimitedPowerKey, kCFBooleanTrue);
rc = doSetProperties(pid, id, dict);
CFRelease(dict);
}
return rc;
}
static void __DarkWakeHandleTeardownCallback(CFRunLoopTimerRef timer __unused, void *info)
{
IOPMAssertionID tearItDown = (int)(uintptr_t)info;
doRelease(getpid(), tearItDown);
}
static void _DarkWakeHandleNetworkWake(void)
{
IOPMAssertionID darkWakeNetworkAssertion = kIOPMNullAssertionID;
if (kIOReturnSuccess == _localCreateAssertion(kIOPMAssertionTypePreventSystemSleep, kIOPMAssertionLevelOn,
CFSTR("PM configd - network wake delay"), &darkWakeNetworkAssertion))
{
CFRunLoopTimerContext timerArgument;
CFRunLoopTimerRef timerTime = NULL;
bzero(&timerArgument, sizeof(timerArgument));
timerArgument.info = (void *)(uintptr_t)darkWakeNetworkAssertion;
timerTime = CFRunLoopTimerCreate(0, CFAbsoluteTimeGetCurrent() + (double)kDarkWakeNetworkHoldForSeconds,
0.0, 0, 0, __DarkWakeHandleTeardownCallback, &timerArgument);
if (timerTime) {
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timerTime, kCFRunLoopDefaultMode);
CFRelease(timerTime);
}
}
return ;
}
typedef struct notifyRegInfo {
IONotificationPortRef port;
io_object_t handle;
} notifyRegInfo_st;
static void _DeregisterForNotification(
notifyRegInfo_st *notifyInfo)
{
if (notifyInfo->handle)
IOObjectRelease(notifyInfo->handle);
if (notifyInfo->port)
IONotificationPortDestroy(notifyInfo->port);
if (notifyInfo)
free(notifyInfo);
}
static notifyRegInfo_st * _RegisterForNotification(
const io_string_t path, const io_name_t interestType,
IOServiceInterestCallback callback, void *refCon)
{
io_service_t obj = MACH_PORT_NULL;
notifyRegInfo_st *notifyInfo = NULL;
kern_return_t kr;
notifyInfo = calloc(sizeof(notifyRegInfo_st), 1);
if ( !notifyInfo )
goto exit;
notifyInfo->port = IONotificationPortCreate( kIOMasterPortDefault );
if ( !notifyInfo->port )
goto exit;
obj = IORegistryEntryFromPath( kIOMasterPortDefault, path);
if ( !obj )
goto exit;
kr = IOServiceAddInterestNotification(
notifyInfo->port,
obj, interestType,
callback, refCon,
¬ifyInfo->handle );
if (kr != KERN_SUCCESS)
goto exit;
IOObjectRelease(obj);
return notifyInfo;
exit:
if (notifyInfo->handle)
IOObjectRelease(notifyInfo->handle);
if (notifyInfo->port)
IONotificationPortDestroy(notifyInfo->port);
if (obj)
IOObjectRelease(obj);
if (notifyInfo)
free(notifyInfo);
return NULL;
}
typedef struct devEnumInfo {
dispatch_source_t dispSrc;
bool suspended;
dispatch_source_t dispSrc2;
notifyRegInfo_st *notifyInfo;
IOPMAssertionID assertId;
}devEnumInfo_st;
static void devEnumerationDone( devEnumInfo_st *deInfo )
{
if (deInfo->dispSrc && (dispatch_source_testcancel(deInfo->dispSrc) == 0)) {
dispatch_source_cancel(deInfo->dispSrc);
}
if (deInfo->dispSrc2 && (dispatch_source_testcancel(deInfo->dispSrc2) == 0)) {
dispatch_source_cancel(deInfo->dispSrc2);
}
if (deInfo->notifyInfo) {
_DeregisterForNotification(deInfo->notifyInfo);
deInfo->notifyInfo = 0;
}
if (deInfo->assertId) {
doRelease(getpid(), deInfo->assertId);
deInfo->assertId = 0;
free(deInfo);
}
}
void ioKitStateCallback (
void * refcon,
io_service_t service,
uint32_t messageType,
void * messageArgument )
{
devEnumInfo_st *deInfo = (devEnumInfo_st *)refcon;
long state = (long)messageArgument;
dispatch_source_t dispSrc;
if (messageType != kIOMessageServiceBusyStateChange)
return;
if (state) {
if (deInfo->dispSrc && !deInfo->suspended) {
dispatch_suspend(deInfo->dispSrc);
deInfo->suspended = true;
}
}
else {
if (deInfo->dispSrc == 0) {
dispSrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0,
0, dispatch_get_main_queue());
dispatch_source_set_event_handler(dispSrc, ^{
devEnumerationDone(deInfo);
});
dispatch_source_set_cancel_handler(dispSrc, ^{
dispatch_release(dispSrc);
});
deInfo->dispSrc = dispSrc;
deInfo->suspended = true;
}
if (deInfo->suspended) {
dispatch_source_set_timer(deInfo->dispSrc, dispatch_time(DISPATCH_TIME_NOW, 5LL * NSEC_PER_SEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(deInfo->dispSrc);
}
deInfo->suspended = false;
}
}
static void _AssertForDeviceEnumeration( )
{
IOPMAssertionID deviceEnumerationAssertion = kIOPMNullAssertionID;
notifyRegInfo_st *notifyInfo = NULL;
devEnumInfo_st *deInfo = NULL;
IOReturn rc;
dispatch_source_t dispSrc;
rc = _localCreateAssertion(kIOPMAssertionTypePreventSystemSleep, kIOPMAssertionLevelOn,
CFSTR("PM configd - Wait for Device enumeration"),
&deviceEnumerationAssertion);
if (rc != kIOReturnSuccess)
return;
_enableAssertionForLimitedPower(getpid(), deviceEnumerationAssertion);
deInfo = calloc(sizeof(devEnumInfo_st), 1);
if (!deInfo) {
goto exit;
}
notifyInfo = _RegisterForNotification(kIOServicePlane ":/",
kIOBusyInterest, ioKitStateCallback, (void *)deInfo);
if ( !notifyInfo ) {
goto exit;
}
deInfo->notifyInfo = notifyInfo;
deInfo->assertId = deviceEnumerationAssertion;
dispSrc = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0,
0, dispatch_get_main_queue());
dispatch_source_set_timer(dispSrc,
dispatch_time(DISPATCH_TIME_NOW, kDeviceEnumerationHoldForSeconds * NSEC_PER_SEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_source_set_event_handler(dispSrc, ^{
devEnumerationDone(deInfo);
});
dispatch_source_set_cancel_handler(dispSrc, ^{
dispatch_release(dispSrc);
});
deInfo->dispSrc2 = dispSrc;
dispatch_resume(deInfo->dispSrc2);
IONotificationPortSetDispatchQueue(notifyInfo->port, dispatch_get_main_queue());
return;
exit:
if (deInfo)
free(deInfo);
if (notifyInfo)
_DeregisterForNotification(notifyInfo);
doRelease(getpid(), deviceEnumerationAssertion);
}
#define IS_DARK_STATE(cap) ( (cap & kIOPMSystemCapabilityCPU) && !(cap & kIOPMSystemCapabilityGraphics) )
#define IS_OFF_STATE(cap) ( 0 == (cap & kIOPMSystemCapabilityCPU) )
__private_extern__ void _ProxyAssertions(const struct IOPMSystemCapabilityChangeParameters *capArgs)
{
CFStringRef wakeType = NULL;
CFStringRef wakeReason = NULL;
IOPMAssertionID pushSvcAssert = kIOPMNullAssertionID;
if ( !(kIOPMSystemCapabilityDidChange & capArgs->changeFlags) )
return;
if ( IS_DARK_STATE(capArgs->toCapabilities) &&
IS_OFF_STATE(capArgs->fromCapabilities) )
{
wakeType = _copyRootDomainProperty(CFSTR(kIOPMRootDomainWakeTypeKey));
wakeReason = _copyRootDomainProperty(CFSTR(kIOPMRootDomainWakeReasonKey));
if (isA_CFString(wakeReason) && CFEqual(wakeReason, kIORootDomainWakeReasonDarkPME))
{
if (isA_CFString(wakeType) && CFEqual(wakeType, kIOPMRootDomainWakeTypeNetwork))
_DarkWakeHandleNetworkWake( );
else
_AssertForDeviceEnumeration( );
}
else if (isA_CFString(wakeType))
{
if (CFEqual(wakeType, kIOPMRootDomainWakeTypeNetwork))
_DarkWakeHandleNetworkWake( );
else if (CFEqual(wakeType, kIOPMRootDomainWakeTypeMaintenance) &&
CFEqual(wakeReason, kIOPMRootDomainWakeReasonRTC))
{
_localCreateAssertionWithTimer(kIOPMAssertionTypeBackgroundTask, kIOPMAssertionLevelOn,
CFSTR("Powerd - Wait for client BackgroundTask assertions"), 10, &pushSvcAssert);
}
else if ((CFEqual(wakeType, kIOPMRootDomainWakeTypeAlarm) == false ) &&
(CFEqual(wakeType, kIOPMRootDomainWakeTypeMaintenance) == false ) &&
(CFEqual(wakeType, kIOPMRootDomainWakeTypeSleepTimer) == false ) &&
(CFEqual(wakeType, kIOPMRootDomainWakeTypeSleepService) == false ) &&
(CFEqual(wakeType, kIOPMrootDomainWakeTypeLowBattery) == false ) )
{
_AssertForDeviceEnumeration( );
}
}
else
{
_AssertForDeviceEnumeration( );
}
if (wakeType) {
CFRelease(wakeType);
}
if (wakeReason)
CFRelease(wakeReason);
}
}
void delayDisplayTurnOff( )
{
CFNumberRef levelNum = NULL;
CFNumberRef Timeout_num = NULL;
CFTimeInterval delay = 0;
IOPMAssertionLevel level = kIOPMAssertionLevelOn;
static IOPMAssertionID id = kIOPMNullAssertionID;
CFMutableDictionaryRef dict = NULL;
delay = gDisplaySleepTimer > kPMMaxDisplayTurnOffDelay ?
kPMMaxDisplayTurnOffDelay : gDisplaySleepTimer;
delay *= 60;
if (id == kIOPMNullAssertionID)
{
dict = _IOPMAssertionDescriptionCreate(
kIOPMAssertionTypePreventUserIdleDisplaySleep,
CFSTR("com.apple.powermanagement.delayDisplayOff"),
NULL, CFSTR("Proxy to delay display off"), NULL,
(CFTimeInterval)delay, kIOPMAssertionTimeoutActionTurnOff);
if (dict) {
doCreate(getpid(), dict, &id);
CFRelease(dict);
}
}
else
{
dict = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
levelNum = CFNumberCreate(0, kCFNumberIntType, &level);
Timeout_num = CFNumberCreate(0, kCFNumberDoubleType, &delay);
if (dict && levelNum && Timeout_num)
{
CFDictionarySetValue(dict, kIOPMAssertionLevelKey, levelNum);
CFDictionarySetValue(dict, kIOPMAssertionTimeoutKey, Timeout_num);
doSetProperties(getpid(), id, dict);
}
if (dict) CFRelease(dict);
if (levelNum) CFRelease(levelNum);
if (Timeout_num) CFRelease(Timeout_num);
}
}
static bool propertiesDictRequiresRoot(CFDictionaryRef props)
{
if ( CFDictionaryGetValue(props, kIOPMInflowDisableAssertion)
|| CFDictionaryGetValue(props, kIOPMChargeInhibitAssertion) )
{
return true;
} else {
return false;
}
}
typedef struct {
CFStringRef name;
dispatch_source_t disp_src;
} ProcessInfoStruct;
static void processInfoCreate(pid_t p, dispatch_source_t d)
{
ProcessInfoStruct proc;
CFDataRef tmpData;
char name[kProcNameBufLen];
proc.disp_src = d;
proc_name(p, name, sizeof(name));
proc.name = CFStringCreateWithCString(0, name, kCFStringEncodingUTF8);
tmpData= CFDataCreate(0, (uint8_t *)(&proc), sizeof(ProcessInfoStruct));
if (tmpData) {
CFDictionarySetValue(gProcessDict, (const void *)p, tmpData);
CFRelease(tmpData);
}
}
static bool processInfoRetain(pid_t p)
{
CFDataRef tmpData = CFDictionaryGetValue(gProcessDict, (const void *)p);
if (tmpData) {
CFRetain(tmpData);
return true;
}
return false;
}
CFStringRef processInfoGetName(pid_t p)
{
CFDataRef tmpData = CFDictionaryGetValue(gProcessDict, (const void *)p);
ProcessInfoStruct *proc = NULL;
CFStringRef retString = NULL;
if (tmpData && (proc = (ProcessInfoStruct *)CFDataGetBytePtr(tmpData))) {
retString = proc->name;
}
return retString;
}
static void processInfoRelease(pid_t p)
{
ProcessInfoStruct *proc = NULL;
CFIndex cnt;
CFDataRef tmpData = CFDictionaryGetValue(gProcessDict, (const void *)p);
if (!tmpData) return;
cnt = CFGetRetainCount(tmpData);
if (cnt == 1) {
proc = (ProcessInfoStruct *)CFDataGetBytePtr(tmpData);
dispatch_release(proc->disp_src);
CFRelease(proc->name);
CFDictionaryRemoveValue(gProcessDict, (const void *)p);
}
else {
CFRelease(tmpData);
}
return ;
}
static IOReturn lookupAssertion(pid_t pid, IOPMAssertionID id, assertion_t **assertion)
{
unsigned int idx = INDEX_FROM_ID(id);
assertion_t *tmp_a = NULL;
if (idx >= kMaxAssertions)
return kIOReturnBadArgument;
if( CFDictionaryGetValueIfPresent(gAssertionsArray,
(void *)idx, (const void **)&tmp_a) == false)
return kIOReturnBadArgument;
if (tmp_a->pid != pid)
return kIOReturnNotPermitted;
*assertion = tmp_a;
return kIOReturnSuccess;
}
kern_return_t _io_pm_declare_user_active
(
mach_port_t server __unused,
audit_token_t token,
int user_type,
vm_offset_t props,
mach_msg_type_number_t propsCnt,
int *assertion_id,
int *return_code
)
{
CFMutableDictionaryRef assertionProperties = NULL;
assertion_t *assertion = NULL;
CFDataRef unfolder = NULL;
pid_t callerPID = -1;
IOReturn ret;
bool create_new = true;
uint32_t displaySleepTimerSecs;
CFNumberRef CFdisplaySleepTimer = NULL;
CFDateRef start_date = NULL;
audit_token_to_au32(token, NULL, NULL, NULL, NULL, NULL, &callerPID, NULL, NULL);
unfolder = CFDataCreateWithBytesNoCopy(0, (const UInt8 *)props, propsCnt, kCFAllocatorNull);
if (unfolder) {
assertionProperties = (CFMutableDictionaryRef)CFPropertyListCreateWithData(0, unfolder,
kCFPropertyListMutableContainersAndLeaves, NULL, NULL);
CFRelease(unfolder);
}
if (!assertionProperties) {
*return_code = kIOReturnBadArgument;
goto exit;
}
displaySleepTimerSecs = gDisplaySleepTimer * 60;
CFdisplaySleepTimer = CFNumberCreate(0, kCFNumberIntType, &displaySleepTimerSecs);
CFDictionarySetValue(assertionProperties, kIOPMAssertionTimeoutKey, CFdisplaySleepTimer);
CFRelease(CFdisplaySleepTimer);
CFDictionarySetValue(assertionProperties, kIOPMAssertionTimeoutActionKey, kIOPMAssertionTimeoutActionRelease);
do {
if (assertion_id == NULL || *assertion_id == kIOPMNullAssertionID)
break;
ret = lookupAssertion(callerPID, *assertion_id, &assertion);
if ((kIOReturnSuccess != ret) || !assertion)
break;
if (assertion->kassert != kDeclareUserActivity)
break;
int k = kIOPMAssertionLevelOn;
CFNumberRef useLevelOnNum = CFNumberCreate(0, kCFNumberIntType, &k);
CFDictionarySetValue(assertion->props, kIOPMAssertionLevelKey, useLevelOnNum);
CFRelease(useLevelOnNum);
start_date = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
if (start_date) {
CFDictionarySetValue(assertionProperties, kIOPMAssertionCreateDateKey, start_date);
CFRelease(start_date);
}
*return_code = doSetProperties(callerPID, *assertion_id, assertionProperties);
create_new = false;
} while (false);
if (create_new) {
CFDictionarySetValue(assertionProperties, kIOPMAssertionTypeKey, kIOPMAssertionUserIsActive);
*return_code = doCreate(callerPID, assertionProperties, (IOPMAssertionID *)assertion_id);
}
exit:
if (assertionProperties)
CFRelease(assertionProperties);
vm_deallocate(mach_task_self(), props, propsCnt);
return KERN_SUCCESS;
}
static uint64_t getMonotonicTime( )
{
static mach_timebase_info_data_t timebaseInfo;
if (timebaseInfo.denom == 0)
mach_timebase_info(&timebaseInfo);
return ( (mach_absolute_time( ) * timebaseInfo.numer) / (timebaseInfo.denom * NSEC_PER_SEC));
}
void insertInactiveAssertion(assertion_t *assertion, assertionType_t *assertType)
{
LIST_INSERT_HEAD(&assertType->inactive, assertion, link);
assertion->state &= ~kAssertionStateTimed;
assertion->state |= kAssertionStateInactive;
}
void removeInactiveAssertion(assertion_t *assertion, assertionType_t *assertType)
{
LIST_REMOVE(assertion, link);
assertion->state &= ~kAssertionStateInactive;
}
void insertActiveAssertion(assertion_t *assertion, assertionType_t *assertType)
{
LIST_INSERT_HEAD(&assertType->active, assertion, link);
assertion->state &= ~(kAssertionStateTimed|kAssertionStateInactive);
if ( (assertType->flags & kAssertionTypeNotValidOnBatt) &&
(assertion->state & kAssertionStateValidOnBatt) )
assertType->validOnBattCount++;
}
void removeActiveAssertion(assertion_t *assertion, assertionType_t *assertType)
{
LIST_REMOVE(assertion, link);
if ( (assertion->state & kAssertionStateValidOnBatt) && assertType->validOnBattCount)
assertType->validOnBattCount--;
}
void resetAssertionTimer(assertionType_t *assertType)
{
uint64_t currTime, timeout ;
assertion_t *nextAssertion = NULL;
nextAssertion = LIST_FIRST(&assertType->activeTimed);
if (!nextAssertion) return;
timeout = nextAssertion->timeout;
currTime = getMonotonicTime();
if (nextAssertion->timeout <= currTime) {
CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, ^{ handleAssertionTimeout(assertType); });
CFRunLoopWakeUp(_getPMRunLoop());
}
else {
dispatch_suspend(assertType->timer);
dispatch_source_set_timer(assertType->timer,
dispatch_time(DISPATCH_TIME_NOW, (nextAssertion->timeout-currTime)*NSEC_PER_SEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(assertType->timer);
}
}
static void releaseAssertionMemory(assertion_t *assertion)
{
int idx = INDEX_FROM_ID(assertion->assertionId);
assertion_t *tmp_a = NULL;
if ( (idx < 0) || (idx >= kMaxAssertions) ||
(CFDictionaryGetValueIfPresent(gAssertionsArray,
(void *)idx, (const void **)&tmp_a) == false) || (tmp_a != assertion) ) {
#ifdef DEBUG
abort();
#endif
return; }
logASLAssertionEvent(kPMASLAssertionActionRelease, assertion);
CFDictionaryRemoveValue(gAssertionsArray, (void *)idx);
if (assertion->props) CFRelease(assertion->props);
free(assertion);
processInfoRelease(assertion->pid);
}
void handleAssertionTimeout(assertionType_t *assertType)
{
assertion_t *assertion;
CFDateRef dateNow = NULL;
uint64_t currtime = getMonotonicTime( );
uint32_t timedoutCnt = 0;
CFStringRef timeoutAction = NULL;
bool displayProxy = false;
while( (assertion = LIST_FIRST(&assertType->activeTimed)) )
{
if (assertion->timeout > currtime) {
assertion = NULL;
break;
}
timedoutCnt++;
LIST_REMOVE(assertion, link);
assertion->state &= ~kAssertionStateTimed;
if ( (assertion->state & kAssertionStateValidOnBatt) && assertType->validOnBattCount)
assertType->validOnBattCount--;
if ((dateNow = CFDateCreate(0, CFAbsoluteTimeGetCurrent()))) {
CFDictionarySetValue(assertion->props, kIOPMAssertionTimedOutDateKey, dateNow);
CFRelease(dateNow);
}
appendTimedOutAssertion(assertion->props);
logASLAssertionEvent(kPMASLAssertionActionTimeOut, assertion);
if ( (assertion->kassert == kPreventDisplaySleepIndex) && (assertion->pid != getpid()))
displayProxy = true;
timeoutAction = CFDictionaryGetValue(assertion->props, kIOPMAssertionTimeoutActionKey);
if (isA_CFString(timeoutAction) && CFEqual(kIOPMAssertionTimeoutActionRelease, timeoutAction))
{
releaseAssertionMemory(assertion);
}
else
{
insertInactiveAssertion(assertion, assertType);
if (isA_CFString(timeoutAction) && CFEqual(kIOPMAssertionTimeoutActionKillProcess, timeoutAction))
{
kill(assertion->pid, SIGTERM);
}
}
}
if ( !timedoutCnt ) return;
resetAssertionTimer(assertType);
if (displayProxy) delayDisplayTurnOff( );
if (assertType->handler)
(*assertType->handler)(assertType, kAssertionOpRelease);
logASLAssertionsAggregate();
notify_post( kIOPMAssertionTimedOutNotifyString );
notify_post( kIOPMAssertionsAnyChangedNotifyString );
}
void removeTimedAssertion(assertion_t *assertion, assertionType_t *assertType)
{
bool adjustTimer = false;
if (LIST_FIRST(&assertType->activeTimed) == assertion) {
adjustTimer = true;
}
LIST_REMOVE(assertion, link);
assertion->state &= ~kAssertionStateTimed;
if ( (assertion->state & kAssertionStateValidOnBatt) && assertType->validOnBattCount)
assertType->validOnBattCount--;
if (adjustTimer) resetAssertionTimer(assertType);
}
void updateAssertionTimer(assertionType_t *assertType)
{
uint64_t currTime;
assertion_t *assertion = NULL;
if ((assertion = LIST_FIRST(&assertType->activeTimed)) == NULL) return;
if (assertType->timer == NULL) {
assertType->timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(assertType->timer, ^{
handleAssertionTimeout(assertType);
});
dispatch_source_set_cancel_handler(assertType->timer, ^{
dispatch_release(assertType->timer);
});
}
else {
dispatch_suspend(assertType->timer);
}
currTime = getMonotonicTime();
if (assertion->timeout <= currTime) {
dispatch_resume(assertType->timer);
CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, ^{ handleAssertionTimeout(assertType); });
CFRunLoopWakeUp(_getPMRunLoop());
}
else {
dispatch_source_set_timer(assertType->timer,
dispatch_time(DISPATCH_TIME_NOW, (assertion->timeout-currTime)*NSEC_PER_SEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(assertType->timer);
}
}
void insertTimedAssertion(assertion_t *assertion, assertionType_t *assertType, bool updateTimer)
{
assertion_t *a, *prev;
if (LIST_EMPTY(&assertType->activeTimed) ) {
LIST_INSERT_HEAD(&assertType->activeTimed, assertion, link);
}
else {
LIST_FOREACH(a, &assertType->activeTimed, link)
{
prev = a;
if (a->timeout > assertion->timeout)
break;
}
if (a)
LIST_INSERT_BEFORE(a, assertion, link);
else
LIST_INSERT_AFTER(prev, assertion, link);
}
assertion->state |= kAssertionStateTimed;
if ( (assertType->flags & kAssertionTypeNotValidOnBatt) &&
(assertion->state & kAssertionStateValidOnBatt) )
assertType->validOnBattCount++;
if (LIST_FIRST(&assertType->activeTimed) != assertion)
return;
if (updateTimer) updateAssertionTimer(assertType);
return;
}
static void releaseAssertion(assertion_t *assertion, bool callHandler)
{
assertionType_t *assertType;
assertType = &gAssertionTypes[assertion->kassert];
if ( (assertion->kassert == kPreventDisplaySleepIndex) && (assertion->pid != getpid()))
delayDisplayTurnOff( );
if (assertion->state & kAssertionStateTimed)
removeTimedAssertion(assertion, assertType);
else if (assertion->state & kAssertionStateInactive)
removeInactiveAssertion(assertion, assertType);
else
removeActiveAssertion(assertion, assertType);
if (!callHandler) return;
if (assertType->handler)
(*assertType->handler)(assertType, kAssertionOpRelease);
}
static IOReturn doRelease(pid_t pid, IOPMAssertionID id)
{
IOReturn ret;
assertion_t *assertion = NULL;
ret = lookupAssertion(pid, id, &assertion);
if ((kIOReturnSuccess != ret)) {
return ret;
}
if (assertion->retainCnt)
assertion->retainCnt--;
if (assertion->retainCnt) {
return kIOReturnSuccess;
}
releaseAssertion(assertion, true);
releaseAssertionMemory(assertion);
notify_post( kIOPMAssertionsAnyChangedNotifyString );
return kIOReturnSuccess;
}
__private_extern__ void
applyToAllAssertionsSync(assertionType_t *assertType, bool applyToInactives, void (^performOnAssertion)(assertion_t *))
{
assertion_t *assertion, *nextAssertion;
assertion = LIST_FIRST(&assertType->active);
while( assertion )
{
nextAssertion = LIST_NEXT(assertion, link);
performOnAssertion(assertion);
assertion = nextAssertion;
}
assertion = LIST_FIRST(&assertType->activeTimed);
while( assertion )
{
nextAssertion = LIST_NEXT(assertion, link);
performOnAssertion(assertion);
assertion = nextAssertion;
}
if ( !applyToInactives) return;
assertion = LIST_FIRST(&assertType->inactive);
while( assertion )
{
nextAssertion = LIST_NEXT(assertion, link);
performOnAssertion(assertion);
assertion = nextAssertion;
}
}
__private_extern__ void
HandleProcessExit(pid_t deadPID)
{
int i;
assertionType_t *assertType = NULL;
assertion_t *assertion = NULL;
__block LIST_HEAD(, assertion) list = LIST_HEAD_INITIALIZER(list);
for (i=0; i < kIOPMNumAssertionTypes; i++)
{
assertType = &gAssertionTypes[i];
applyToAllAssertionsSync(assertType, true, ^(assertion_t *assertion)
{
if (assertion->pid == deadPID) {
releaseAssertion(assertion, false);
LIST_INSERT_HEAD(&list, assertion, link);
}
});
if (assertType->handler)
(*assertType->handler)(assertType, kAssertionOpRelease);
while( (assertion = LIST_FIRST(&list)) )
{
LIST_REMOVE(assertion, link);
releaseAssertionMemory(assertion);
}
}
notify_post( kIOPMAssertionsAnyChangedNotifyString );
}
static int getAssertionTypeIndex(CFStringRef type)
{
int idx = -1;
CFNumberRef numRef = NULL;
if (!isA_CFString(type))
return -1;
numRef = CFDictionaryGetValue(gUserAssertionTypesDict, type);
if (isA_CFNumber(numRef))
CFNumberGetValue(numRef, kCFNumberIntType, &idx);
if (idx < 0 || idx >= kIOPMNumAssertionTypes)
return -1;
return idx;
}
static void forwardPropertiesToAssertion(const void *key, const void *value, void *context)
{
assertion_t *assertion = (assertion_t *)context;
assertionType_t *assertType = NULL;
CFTimeInterval timeout = 0;
int level, idx;
if (!isA_CFString(key))
return;
if (CFEqual(key, kIOPMAssertionLevelKey)) {
if (!isA_CFNumber(value)) return;
CFNumberGetValue(value, kCFNumberIntType, &level);
if ( (assertion->state & kAssertionStateInactive) && (level == kIOPMAssertionLevelOn) )
{
assertion->state &= ~kAssertionStateInactive;
assertion->mods |= kAssertionModLevel;
}
else if ( !(assertion->state & kAssertionStateInactive) && (level == kIOPMAssertionLevelOff) )
{
assertion->state |= kAssertionStateInactive;
assertion->mods |= kAssertionModLevel;
}
}
else if (CFEqual(key, kIOPMAssertionTimeoutKey)) {
if (!isA_CFNumber(value)) return;
CFNumberGetValue(value, kCFNumberDoubleType, &timeout);
if (timeout) {
assertion->timeout = (uint64_t)timeout + getMonotonicTime(); }
assertion->mods |= kAssertionModTimer;
}
else if (CFEqual(key, kIOPMAssertionAppliesToLimitedPowerKey)) {
if (!isA_CFBoolean(value)) return;
assertType = &gAssertionTypes[assertion->kassert];
if ((assertType->flags & kAssertionTypeNotValidOnBatt) == 0) return;
if ((value == kCFBooleanTrue) && !(assertion->state & kAssertionStateValidOnBatt))
{
assertType->validOnBattCount++;
assertion->state |= kAssertionStateValidOnBatt;
assertion->mods |= kAssertionModPowerConstraint;
}
else if ((value == kCFBooleanFalse) && (assertion->state & kAssertionStateValidOnBatt) )
{
if (assertType->validOnBattCount) assertType->validOnBattCount--;
assertion->state &= ~kAssertionStateValidOnBatt;
assertion->mods |= kAssertionModPowerConstraint;
}
}
else if (CFEqual(key, kIOPMAssertionTypeKey)) {
idx = getAssertionTypeIndex(value);
if ( (idx < 0) || (idx == assertion->kassert) )return;
assertion->kassert = idx;
assertion->state |= kAssertionModType;
}
CFDictionarySetValue(assertion->props, key, value);
}
static IOReturn doSetProperties(pid_t pid,
IOPMAssertionID id,
CFDictionaryRef inProps)
{
assertion_t *assertion = NULL;
assertionType_t *assertType;
uint32_t oldState;
IOReturn ret;
ret = lookupAssertion(pid, id, &assertion);
if ((kIOReturnSuccess != ret)) {
return ret;
}
assertion->mods = 0;
assertType = &gAssertionTypes[assertion->kassert];
oldState = assertion->state;
CFDictionaryApplyFunction(inProps, forwardPropertiesToAssertion,
assertion);
if (assertion->mods & kAssertionModType)
{
releaseAssertion(assertion, true);
assertion->state = 0;
assertion->kassert = 0;
raiseAssertion(assertion);
notify_post( kIOPMAssertionsAnyChangedNotifyString );
return kIOReturnSuccess;
}
if (assertion->mods & kAssertionModLevel)
{
if (assertion->state & kAssertionStateInactive)
{
if (oldState & kAssertionStateTimed)
removeTimedAssertion(assertion, assertType);
else
removeActiveAssertion(assertion, assertType);
insertInactiveAssertion(assertion, assertType);
if ( (assertion->kassert == kPreventDisplaySleepIndex) &&
(assertion->pid != getpid()))
delayDisplayTurnOff( );
if (assertType->handler)
(*assertType->handler)(assertType, kAssertionOpRelease);
logASLAssertionEvent(kPMASLAssertionActionTurnOff, assertion);
}
else
{
removeInactiveAssertion(assertion, assertType);
raiseAssertion(assertion);
logASLAssertionEvent(kPMASLAssertionActionTurnOn, assertion);
}
notify_post( kIOPMAssertionsAnyChangedNotifyString );
return kIOReturnSuccess;
}
if (assertion->state & kAssertionStateInactive)
return kIOReturnSuccess;
if (assertion->mods & kAssertionModTimer)
{
if (oldState & kAssertionStateTimed)
removeTimedAssertion(assertion, assertType);
else
removeActiveAssertion(assertion, assertType);
if (assertion->timeout != 0) {
insertTimedAssertion(assertion, assertType, true);
}
else {
insertActiveAssertion(assertion, assertType);
}
}
if ( (assertion->mods & kAssertionModPowerConstraint) &&
(assertType->handler) )
{
if (assertion->state & kAssertionStateValidOnBatt)
(*assertType->handler)(assertType, kAssertionOpRaise);
else
(*assertType->handler)(assertType, kAssertionOpRelease);
}
notify_post( kIOPMAssertionsAnyChangedNotifyString );
return kIOReturnSuccess;
}
bool checkForActives(assertionType_t *assertType, bool *existsInThisType )
{
bool activeExists = false;
uint32_t idx = 0, linked = assertType->linkedTypes;
if ( (assertType->flags & kAssertionTypeNotValidOnBatt) && ( _getPowerSource() == kBatteryPowered) )
activeExists = (!(assertType->flags & kAssertionTypeDisabled) && (assertType->validOnBattCount > 0));
else
activeExists = (LIST_FIRST(&assertType->active) || LIST_FIRST(&assertType->activeTimed)) &&
!(assertType->flags & kAssertionTypeDisabled);
if (existsInThisType)
*existsInThisType = activeExists;
if (activeExists) return true;
for ( ; linked !=0; linked >>=1, idx++) {
if ((linked & 1) == 0) continue;
assertType = &gAssertionTypes[idx];
if ( (assertType->flags & kAssertionTypeNotValidOnBatt) && ( _getPowerSource() == kBatteryPowered) )
activeExists |= (!(assertType->flags & kAssertionTypeDisabled) && (assertType->validOnBattCount > 0));
else
activeExists |= ((LIST_FIRST(&assertType->active) || LIST_FIRST(&assertType->activeTimed)) &&
!(assertType->flags & kAssertionTypeDisabled) );
if (activeExists) return true;
}
return activeExists;
}
__private_extern__ bool systemBlockedInS0Dark( )
{
return checkForActives(&gAssertionTypes[kPreventSleepIndex], NULL);
}
__private_extern__ bool checkForActivesByType(kerAssertionType type)
{
bool activesForTheType = false;
checkForActives(&gAssertionTypes[type], &activesForTheType);
return activesForTheType;
}
__private_extern__ bool checkForEntriesByType(kerAssertionType type)
{
assertionType_t *assertType = &gAssertionTypes[type];
if (LIST_FIRST(&assertType->active) || LIST_FIRST(&assertType->activeTimed))
return true;
return false;
}
__private_extern__ void disableAssertionType(kerAssertionType type)
{
gAssertionTypes[type].flags |= kAssertionTypeDisabled;
if (gAssertionTypes[type].handler)
gAssertionTypes[type].handler(&gAssertionTypes[type], kAssertionOpEval);
}
__private_extern__ void enableAssertionType(kerAssertionType type)
{
gAssertionTypes[type].flags &= ~kAssertionTypeDisabled;
if (gAssertionTypes[type].handler)
gAssertionTypes[type].handler(&gAssertionTypes[type], kAssertionOpEval);
}
static inline void updateAggregates(assertionType_t *assertType, bool activesForTheType)
{
if (activesForTheType) {
SET_AGGREGATE_LEVEL(assertType->kassert, 1 );
}
else {
if (LEVEL_FOR_BIT(assertType->kassert) && assertType->globalTimeout) {
resetGlobalTimer(assertType, 0);
}
SET_AGGREGATE_LEVEL(assertType->kassert, 0 );
}
}
static void modifySettings(assertionType_t *assertType, assertionOps op)
{
bool activeExists;
bool activesForTheType = false;
int opVal;
bool assertionLevel = 0;
assertionLevel = LEVEL_FOR_BIT(assertType->kassert);
activeExists = checkForActives(assertType, &activesForTheType);
updateAggregates(assertType, activesForTheType);
if (op == kAssertionOpRaise) {
if ( assertionLevel || !activeExists )
return;
opVal = 1;
}
else if (op == kAssertionOpRelease) {
if ( !assertionLevel || activeExists)
return;
opVal = 0;
}
else { if (activeExists) {
if (assertionLevel)
return;
opVal = 1;
}
else {
if (!assertionLevel)
return;
opVal = 0;
}
}
switch(assertType->kassert) {
case kHighPerfIndex:
overrideSetting( kPMForceHighSpeed, opVal);
break;
case kBackgroundTaskIndex:
case kPreventIdleIndex:
overrideSetting(kPMPreventIdleSleep, opVal);
break;
default:
return;
}
activateSettingOverrides();
notify_post( kIOPMAssertionsChangedNotifyString );
return;
}
void handleBatteryAssertions(assertionType_t *assertType, assertionOps op)
{
bool activeExists;
if (op == kAssertionOpEval)
return;
activeExists = checkForActives(assertType, NULL);
if (op == kAssertionOpRaise) {
if ( LEVEL_FOR_BIT(assertType->kassert) || !activeExists )
return;
SET_AGGREGATE_LEVEL(assertType->kassert, 1);
}
else {
if ( !LEVEL_FOR_BIT(assertType->kassert) || activeExists)
return;
SET_AGGREGATE_LEVEL(assertType->kassert, 0);
}
switch(assertType->kassert) {
case kDisableInflowIndex:
sendSmartBatteryCommand( kSBUCInflowDisable,
op == kAssertionOpRaise ? 1 : 0);
break;
case kInhibitChargeIndex:
sendSmartBatteryCommand( kSBUCChargeInhibit,
op == kAssertionOpRaise ? 1 : 0);
break;
case kDisableWarningsIndex:
_setRootDomainProperty( CFSTR("BatteryWarningsDisabled"), kCFBooleanTrue);
break;
case kNoRealPowerSourcesDebugIndex:
switchActiveBatterySet(op == kAssertionOpRaise ? kBatteryShowFake : kBatteryShowReal);
BatteryTimeRemainingBatteriesHaveChanged(NULL);
default:
break;
}
notify_post( kIOPMAssertionsChangedNotifyString );
return;
}
void setKernelAssertions(assertionType_t *assertType, assertionOps op)
{
uint32_t assertBit = 0;
bool activeExists, activesForTheType;
activeExists = checkForActives(assertType, &activesForTheType);
switch(assertType->kassert) {
case kPushServiceTaskIndex:
#if LOG_SLEEPSERVICES
if ( !activesForTheType && LEVEL_FOR_BIT(assertType->kassert) )
logASLMessageSleepServiceTerminated(assertType->forceTimedoutCnt);
#endif
assertBit = kIOPMDriverAssertionCPUBit;
break;
case kPreventSleepIndex:
case kBackgroundTaskIndex:
assertBit = kIOPMDriverAssertionCPUBit;
break;
case kDeclareUserActivity:
case kPreventDisplaySleepIndex:
assertBit = kIOPMDriverAssertionPreventDisplaySleepBit;
break;
case kExternalMediaIndex:
assertBit = kIOPMDriverAssertionExternalMediaMountedBit;
break;
default:
return;
}
updateAggregates(assertType, activesForTheType);
if (op == kAssertionOpRaise) {
if ((assertType->kassert == kDeclareUserActivity) && activesForTheType)
sendActivityTickle();
#if !TARGET_OS_EMBEDDED
if ( (assertType->kassert == kPreventSleepIndex) && activesForTheType)
_unclamp_silent_running();
#endif
if ( (kerAssertionBits & assertBit) || !activeExists )
return;
}
else if (op == kAssertionOpRelease) {
if ( !(kerAssertionBits & assertBit) || activeExists)
return;
}
else {
#if !TARGET_OS_EMBEDDED
if ( (assertType->kassert == kPreventSleepIndex) && activesForTheType)
_unclamp_silent_running();
#endif
if (activeExists && (kerAssertionBits & assertBit))
return;
else if ( !activeExists && !(kerAssertionBits & assertBit) )
return;
}
if (activeExists) {
kerAssertionBits |= assertBit;
sendUserAssertionsToKernel(kerAssertionBits);
}
else {
kerAssertionBits &= ~assertBit;
sendUserAssertionsToKernel(kerAssertionBits);
}
notify_post( kIOPMAssertionsChangedNotifyString );
}
static void enableIdleHandler(assertionType_t *assertType, assertionOps op)
{
static IOPMAssertionID enableIdleId = kIOPMNullAssertionID;
CFMutableDictionaryRef assertionDescription = NULL;
bool activeExists;
#if !TARGET_OS_EMBEDDED
return; #endif
if (op == kAssertionOpEval)
return;
if (assertType->kassert != kEnableIdleIndex)
return;
activeExists = checkForActives(assertType, NULL);
if (op == kAssertionOpRaise) {
if ( !activeExists )
return;
SET_AGGREGATE_LEVEL(assertType->kassert, 1);
InternalReleaseAssertion(&enableIdleId);
}
else {
if ( activeExists )
return;
SET_AGGREGATE_LEVEL(assertType->kassert, 0);
assertionDescription = _IOPMAssertionDescriptionCreate(
kIOPMAssertionTypePreventUserIdleSystemSleep,
CFSTR("com.apple.powermanagement.enableIdle"),
NULL, CFSTR("Waiting for Idle Sleep to enabled"), NULL, 0, 0);
InternalCreateAssertion(assertionDescription, &enableIdleId);
CFRelease(assertionDescription);
}
notify_post( kIOPMAssertionsChangedNotifyString );
}
void enforceAssertionTypeTimeCap(assertionType_t *assertType)
{
assertion_t *assertion = NULL;
assertType->forceTimedoutCnt = 0;
while( (assertion = LIST_FIRST(&assertType->active)) )
{
removeActiveAssertion(assertion, assertType);
insertInactiveAssertion(assertion, assertType);
assertType->forceTimedoutCnt++;
logASLAssertionEvent(kPMASLAssertionActionTimeOut, assertion);
mt2RecordAssertionEvent(kAssertionOpGlobalTimeout, assertion);
}
while( (assertion = LIST_FIRST(&assertType->activeTimed)) )
{
LIST_REMOVE(assertion, link);
assertion->state &= ~kAssertionStateTimed;
insertInactiveAssertion(assertion, assertType);
assertType->forceTimedoutCnt++;
logASLAssertionEvent(kPMASLAssertionActionTimeOut, assertion);
mt2RecordAssertionEvent(kAssertionOpGlobalTimeout, assertion);
}
resetAssertionTimer(assertType);
if (assertType->handler)
(*assertType->handler)(assertType, kAssertionOpRelease);
if (assertType->forceTimedoutCnt == 0)
logASLMessageSleepServiceTerminated(0);
logASLAssertionsAggregate();
}
void resetGlobalTimer(assertionType_t *assertType, uint64_t timeout)
{
assertType->globalTimeout = timeout;
if (assertType->globalTimeout == 0) {
if ( assertType->globalTimer)
dispatch_source_cancel(assertType->globalTimer);
return;
}
if (assertType->globalTimer == NULL) {
assertType->globalTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(assertType->globalTimer, ^{
enforceAssertionTypeTimeCap(assertType);
});
dispatch_source_set_cancel_handler(assertType->globalTimer, ^{
dispatch_release(assertType->globalTimer);
assertType->globalTimer = NULL;
});
}
else {
dispatch_suspend(assertType->globalTimer);
}
dispatch_source_set_timer(assertType->globalTimer,
dispatch_time(DISPATCH_TIME_NOW, assertType->globalTimeout * NSEC_PER_SEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(assertType->globalTimer);
}
static IOReturn raiseAssertion(assertion_t *assertion)
{
int idx = -1;
int level;
bool isFirstOne;
uint64_t currTime = getMonotonicTime();
uint32_t levelInt = 0;
CFDateRef start_date = NULL;
CFStringRef assertionTypeRef;
CFNumberRef numRef = NULL;
CFNumberRef levelNum = NULL;
CFTimeInterval timeout = 0;
assertionType_t *assertType;
uint64_t assertion_id_64;
assertionTypeRef = CFDictionaryGetValue(assertion->props, kIOPMAssertionTypeKey);
idx = getAssertionTypeIndex(assertionTypeRef);
if (idx < 0 )
return kIOReturnBadArgument;
assertType = &gAssertionTypes[idx];
assertion->kassert = idx;
assertion_id_64 = ((uint64_t)idx << 32) | assertion->assertionId;
CFNumberRef uniqueAID = CFNumberCreate(0, kCFNumberSInt64Type, &assertion_id_64);
if (uniqueAID) {
CFDictionarySetValue(assertion->props, kIOPMAssertionGlobalUniqueIDKey, uniqueAID);
CFRelease(uniqueAID);
}
start_date = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
if (start_date) {
CFDictionarySetValue(assertion->props, kIOPMAssertionCreateDateKey, start_date);
CFRelease(start_date);
}
assertion->createTime = currTime;
numRef = CFDictionaryGetValue(assertion->props, kIOPMAssertionLevelKey);
if (isA_CFNumber(numRef)) {
CFNumberGetValue(numRef, kCFNumberIntType, &level);
if (level == kIOPMAssertionLevelOff) {
insertInactiveAssertion(assertion, assertType);
goto exit;
}
}
else {
levelInt = kIOPMAssertionLevelOn;
levelNum = CFNumberCreate(0, kCFNumberIntType, &levelInt);
CFDictionarySetValue(assertion->props, kIOPMAssertionLevelKey, levelNum);
CFRelease(levelNum);
}
if (assertType->flags & kAssertionTypeNotValidOnBatt) {
CFBooleanRef val = NULL;
val = CFDictionaryGetValue(assertion->props, kIOPMAssertionAppliesToLimitedPowerKey);
if (isA_CFBoolean(val) && (val == kCFBooleanTrue)) {
assertion->state |= kAssertionStateValidOnBatt;
}
}
isFirstOne = (LIST_FIRST(&assertType->active) == NULL) &&
(LIST_FIRST(&assertType->activeTimed) == NULL);
numRef = CFDictionaryGetValue(assertion->props, kIOPMAssertionTimeoutKey);
if (isA_CFNumber(numRef))
CFNumberGetValue(numRef, kCFNumberDoubleType, &timeout);
if (timeout) {
assertion->timeout = (uint64_t)timeout+currTime; insertTimedAssertion(assertion, assertType, true);
}
else {
insertActiveAssertion(assertion, assertType);
}
if (assertType->handler)
(*assertType->handler)(assertType, kAssertionOpRaise);
mt2RecordAssertionEvent(kAssertionOpRaise, assertion);
exit:
return kIOReturnSuccess;
}
IOReturn doCreate(
pid_t pid,
CFMutableDictionaryRef newProperties,
IOPMAssertionID *assertion_id
)
{
int i;
dispatch_source_t proc_exit_source = NULL;
assertion_t *assertion = NULL;
assertion_t *tmp_a = NULL;
IOReturn result = kIOReturnSuccess;
*assertion_id = kIOPMNullAssertionID;
if ( !processInfoRetain(pid) ) {
if (( proc_exit_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_PROC, pid, DISPATCH_PROC_EXIT, dispatch_get_main_queue())))
{
dispatch_source_set_event_handler(proc_exit_source, ^{
HandleProcessExit(pid);
});
dispatch_source_set_cancel_handler(proc_exit_source, ^{
dispatch_release(proc_exit_source);
});
processInfoCreate(pid, proc_exit_source);
dispatch_resume(proc_exit_source);
}
}
for (i=gNextAssertionIdx; CFDictionaryGetValueIfPresent(gAssertionsArray,
(void *)i, (const void **)&tmp_a) == true; ) {
i = (i+1) % kMaxAssertions;
if (i == gNextAssertionIdx) break;
}
if (CFDictionaryGetValueIfPresent(gAssertionsArray, (void *)i, (const void **)&tmp_a) == true)
return kIOReturnNoMemory;
assertion = calloc(1, sizeof(assertion_t));
if (assertion == NULL) {
return kIOReturnNoMemory;
}
assertion->pid = pid;
assertion->props = newProperties;
CFRetain(newProperties);
assertion->retainCnt = 1;
assertion->assertionId = ID_FROM_INDEX(i);
CFDictionarySetValue(gAssertionsArray, (void *)i, (const void *)assertion);
gNextAssertionIdx = (i+1) % kMaxAssertions;
result = raiseAssertion(assertion);
if (result != kIOReturnSuccess) {
CFDictionaryRemoveValue(gAssertionsArray, (void *)i);
CFRelease(assertion->props);
free(assertion);
return result;
}
if ( (gDebugFlags & kIOPMDebugLogAssertionSynchronous) ||
(assertion->kassert == kDeclareUserActivity) ||
(assertion->kassert == kPreventSleepIndex) )
logASLAssertionEvent(kPMASLAssertionActionCreate, assertion);
notify_post( kIOPMAssertionsAnyChangedNotifyString );
*assertion_id = assertion->assertionId;
return result;
}
static void copyAssertion(assertion_t *assertion, CFMutableDictionaryRef assertionsDict)
{
bool created = false;
CFNumberRef pidCF = NULL;
CFMutableDictionaryRef processDict = NULL;
CFMutableArrayRef pidAssertionsArr = NULL;
CFStringRef processName = NULL;
pidCF = CFNumberCreate(0, kCFNumberIntType, &assertion->pid);
processDict = (CFMutableDictionaryRef)CFDictionaryGetValue(assertionsDict, pidCF);
if (processDict == NULL)
{
pidAssertionsArr = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
processDict = CFDictionaryCreateMutable( kCFAllocatorDefault, 2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(processDict,
CFSTR("PerTaskAssertions"),
pidAssertionsArr);
CFDictionarySetValue(processDict,
kIOPMAssertionPIDKey,
pidCF);
CFDictionarySetValue(assertionsDict, pidCF, processDict);
created = true;
}
else {
pidAssertionsArr = (CFMutableArrayRef)CFDictionaryGetValue(processDict, CFSTR("PerTaskAssertions"));
}
processName = processInfoGetName(assertion->pid);
if (processName) {
CFDictionarySetValue(assertion->props, kIOPMAssertionProcessNameKey, processName);
}
CFArrayAppendValue(pidAssertionsArr, assertion->props);
CFRelease(pidCF);
if (created) {
CFRelease(pidAssertionsArr);
CFRelease(processDict);
}
}
static CFArrayRef copyPIDAssertionDictionaryFlattened(void)
{
CFMutableDictionaryRef assertionsDict = NULL;
CFMutableArrayRef returnArray = NULL;
CFDictionaryRef *assertionsDictArr;
assertionType_t *assertType = NULL;
int i, count;
returnArray = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
assertionsDict = CFDictionaryCreateMutable(
kCFAllocatorDefault, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
for (i=0; i < kIOPMNumAssertionTypes; i++)
{
if (i == kEnableIdleIndex) continue;
assertType = &gAssertionTypes[i];
applyToAllAssertionsSync(assertType, false, ^(assertion_t *assertion)
{
copyAssertion(assertion, assertionsDict);
});
}
count = CFDictionaryGetCount(assertionsDict);
assertionsDictArr = (CFDictionaryRef *)malloc(sizeof(CFDictionaryRef)*count);
CFDictionaryGetKeysAndValues(assertionsDict,
NULL, (const void **)assertionsDictArr);
for (i=0; i < count; i++) {
CFArrayAppendValue(returnArray, assertionsDictArr[i]);
}
free(assertionsDictArr);
CFRelease(assertionsDict);
return returnArray;
}
static IOReturn copyAssertionForID(
pid_t inPID, int inID,
CFMutableDictionaryRef *outAssertion)
{
IOReturn ret = kIOReturnBadArgument;
assertion_t *assertion = NULL;
if (outAssertion) {
*outAssertion = NULL;
}
else goto exit;
ret = lookupAssertion(inPID, inID, &assertion);
if ((kIOReturnSuccess != ret)) {
ret = kIOReturnNotFound;
goto exit;
}
CFRetain(assertion->props);
*outAssertion = assertion->props;
exit:
return ret;
}
static IOReturn doRetain(pid_t pid, IOPMAssertionID id)
{
IOReturn ret;
assertion_t *assertion = NULL;
ret = lookupAssertion(pid, id, &assertion);
if ((kIOReturnSuccess != ret)) {
return ret;
}
assertion->retainCnt++;
notify_post( kIOPMAssertionsAnyChangedNotifyString );
return kIOReturnSuccess;
}
__private_extern__ void evalAllUserActivityAssertions(unsigned int dispSlpTimer)
{
assertionType_t *assertType;
int changeInSecs;
assertion_t *assertion, *nextAssertion;
uint64_t currTime = getMonotonicTime();
if (gDisplaySleepTimer == (int)dispSlpTimer)
return;
changeInSecs = ((int)dispSlpTimer - gDisplaySleepTimer) * 60;
gDisplaySleepTimer = dispSlpTimer;
assertType = &gAssertionTypes[kDeclareUserActivity];
assertion = LIST_FIRST(&assertType->activeTimed);
while( assertion )
{
if (assertion->timeout + changeInSecs < currTime)
assertion->timeout = currTime;
else
assertion->timeout += changeInSecs;
assertion = LIST_NEXT(assertion, link);
}
assertion = LIST_FIRST(&assertType->active);
while( assertion )
{
nextAssertion = LIST_NEXT(assertion, link);
if (changeInSecs < 0)
assertion->timeout = currTime;
else
assertion->timeout = changeInSecs;
removeActiveAssertion(assertion, assertType);
insertTimedAssertion(assertion, assertType, false);
assertion = nextAssertion;
}
updateAssertionTimer(assertType);
if (assertType->handler)
(*assertType->handler)(assertType, kAssertionOpRelease);
}
static void evaluateAssertions(void)
{
int i, pwrSrc;
static int prevPwrSrc = -1;
assertionType_t *assertType;
pwrSrc = _getPowerSource();
if (pwrSrc == prevPwrSrc)
return;
prevPwrSrc = pwrSrc;
for (i=0; i < kIOPMNumAssertionTypes; i++)
{
assertType = &gAssertionTypes[i];
if (assertType->handler) {
(*assertType->handler)(assertType, kAssertionOpEval);
}
}
configAssertionType(kBackgroundTaskIndex, false);
cancelPowerNapStates( );
logASLAssertionsAggregate();
}
__private_extern__ void setSleepServicesTimeCap(uint32_t timeoutInMS)
{
assertionType_t *assertType;
assertType = &gAssertionTypes[kPushServiceTaskIndex];
if ( (timeoutInMS == 0) && (assertType->globalTimeout == 0) )
return;
resetGlobalTimer(assertType, timeoutInMS/1000);
if (timeoutInMS == 0) {
CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode,
^{ enforceAssertionTypeTimeCap(assertType); });
CFRunLoopWakeUp(_getPMRunLoop());
}
}
#if !TARGET_OS_EMBEDDED
void assertionLogger( )
{
static uint32_t idxToLog = 0;
uint64_t currTime = getMonotonicTime();
uint32_t delay = ASSERTION_LOG_DELAY;
assertion_t *assertion = NULL;
if (gDebugFlags & kIOPMDebugLogAssertionSynchronous)
idxToLog = gNextAssertionIdx;
while (idxToLog != gNextAssertionIdx)
{
if ( (CFDictionaryGetValueIfPresent(gAssertionsArray,
(void *)idxToLog, (const void **)&assertion) == false)
|| (assertion->state & kAssertionStateLogged) )
{
idxToLog = (idxToLog+1) % kMaxAssertions;
continue;
}
if ((currTime - assertion->createTime) < ASSERTION_LOG_DELAY)
{
delay = currTime - assertion->createTime;
break;
}
logASLAssertionEvent(kPMASLAssertionActionCreate, assertion);
idxToLog = (idxToLog+1) % kMaxAssertions;
}
dispatch_source_set_timer(logDispatch,
dispatch_time(DISPATCH_TIME_NOW, delay*NSEC_PER_SEC),
DISPATCH_TIME_FOREVER, 0);
}
#endif
__private_extern__ void configAssertionType(kerAssertionType idx, bool initialConfig)
{
assertionHandler_f oldHandler = NULL;
CFNumberRef idxRef = NULL;
uint32_t oldLinks = 0, newLinks = 0;
uint32_t oldFlags, flags, i;
kerAssertionType altIdx;
if ( !gUserAssertionTypesDict )
return;
if (!initialConfig) {
oldLinks = gAssertionTypes[idx].linkedTypes;
oldHandler = gAssertionTypes[idx].handler;
oldFlags = gAssertionTypes[idx].flags;
}
switch(idx)
{
case kHighPerfIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeNeedsCPU, idxRef);
gAssertionTypes[idx].handler = modifySettings;
gAssertionTypes[idx].kassert = idx;
break;
case kPreventIdleIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypePreventUserIdleSystemSleep, idxRef);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeNoIdleSleep, idxRef);
gAssertionTypes[idx].handler = modifySettings;
gAssertionTypes[idx].kassert = idx;
if (!_DWBT_enabled()) {
newLinks = 1 << kBackgroundTaskIndex;
}
break;
case kDisableInflowIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeDisableInflow, idxRef);
gAssertionTypes[idx].handler = handleBatteryAssertions;
gAssertionTypes[idx].kassert = idx;
break;
case kInhibitChargeIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeInhibitCharging, idxRef);
gAssertionTypes[idx].handler = handleBatteryAssertions;
gAssertionTypes[idx].kassert = idx;
break;
case kDisableWarningsIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeDisableLowBatteryWarnings, idxRef);
gAssertionTypes[idx].handler = handleBatteryAssertions;
gAssertionTypes[idx].kassert = idx;
break;
case kPreventDisplaySleepIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypePreventUserIdleDisplaySleep, idxRef);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeNoDisplaySleep, idxRef);
gAssertionTypes[idx].handler = setKernelAssertions;
newLinks = 1 << kDeclareUserActivity;
gAssertionTypes[idx].kassert = idx;
break;
case kEnableIdleIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeEnableIdleSleep, idxRef);
gAssertionTypes[idx].handler = enableIdleHandler;
gAssertionTypes[idx].kassert = idx;
break;
case kNoRealPowerSourcesDebugIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeDisableRealPowerSources_Debug, idxRef);
gAssertionTypes[idx].handler = handleBatteryAssertions;
gAssertionTypes[idx].kassert = idx;
break;
case kPreventSleepIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypePreventSystemSleep, idxRef);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeDenySystemSleep, idxRef);
gAssertionTypes[idx].flags |= kAssertionTypeNotValidOnBatt;
gAssertionTypes[idx].handler = setKernelAssertions;
newLinks = 1 << kPushServiceTaskIndex;
if (_DWBT_enabled())
newLinks |= 1 << kBackgroundTaskIndex;
gAssertionTypes[idx].kassert = idx;
break;
case kExternalMediaIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, _kIOPMAssertionTypeExternalMedia, idxRef);
gAssertionTypes[idx].handler = setKernelAssertions;
gAssertionTypes[idx].kassert = idx;
break;
case kDeclareUserActivity:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionUserIsActive, idxRef);
gAssertionTypes[idx].handler = setKernelAssertions;
newLinks = 1 << kPreventDisplaySleepIndex;
gAssertionTypes[idx].kassert = idx;
break;
case kPushServiceTaskIndex:
if ( isA_SleepSrvcWake() && _SS_allowed() ) {
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeApplePushServiceTask, idxRef);
}
else {
altIdx = kBackgroundTaskIndex;
idxRef = CFNumberCreate(0, kCFNumberIntType, &altIdx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeApplePushServiceTask, idxRef);
}
gAssertionTypes[idx].flags |= kAssertionTypeGloballyTimed ;
gAssertionTypes[idx].handler = setKernelAssertions;
newLinks = 1 << kPreventSleepIndex;
if (_DWBT_enabled())
newLinks |= 1 << kBackgroundTaskIndex;
gAssertionTypes[idx].kassert = idx;
break;
case kBackgroundTaskIndex:
idxRef = CFNumberCreate(0, kCFNumberIntType, &idx);
CFDictionarySetValue(gUserAssertionTypesDict, kIOPMAssertionTypeBackgroundTask, idxRef);
gAssertionTypes[idx].flags |= kAssertionTypeNotValidOnBatt;
gAssertionTypes[idx].flags &= ~kAssertionTypeDisabled;
if (_DWBT_enabled()) {
gAssertionTypes[idx].handler = setKernelAssertions;
newLinks = 1 << kPreventSleepIndex | 1 << kPushServiceTaskIndex;
if ( !isA_BTMtnceWake() ) {
gAssertionTypes[idx].flags |= kAssertionTypeDisabled;
}
}
else {
gAssertionTypes[idx].handler = modifySettings;
newLinks = 1 << kPreventIdleIndex;
}
gAssertionTypes[idx].kassert = idx;
break;
default:
return;
}
if (idxRef)
CFRelease(idxRef);
if ( (!initialConfig) && (newLinks != oldLinks) ) {
uint32_t links = newLinks;
for (i = 0; oldLinks; i++, oldLinks >>= 1) {
if ( !(oldLinks & 1) ) continue;
gAssertionTypes[i].linkedTypes &= ~(1 << idx);
}
for (i = 0 ; links; i++, links >>= 1) {
if ( !(links & 1) ) continue;
gAssertionTypes[i].linkedTypes |= (1 << idx);
}
}
if (initialConfig) {
gAssertionTypes[idx].linkedTypes = newLinks;
}
else if (oldHandler != gAssertionTypes[idx].handler) {
flags = gAssertionTypes[idx].flags;
gAssertionTypes[idx].flags |= kAssertionTypeDisabled;
oldHandler(&gAssertionTypes[idx], kAssertionOpEval);
gAssertionTypes[idx].flags = flags;
gAssertionTypes[idx].linkedTypes = newLinks;
if (gAssertionTypes[idx].handler)
gAssertionTypes[idx].handler(&gAssertionTypes[idx], kAssertionOpEval);
}
else if (oldFlags != gAssertionTypes[idx].flags) {
if (gAssertionTypes[idx].handler)
gAssertionTypes[idx].handler(&gAssertionTypes[idx], kAssertionOpEval);
}
}
__private_extern__ void
PMAssertions_prime(void)
{
kerAssertionType idx = 0;
gAssertionsArray = CFDictionaryCreateMutable(NULL, kMaxAssertions, NULL, NULL);
gProcessDict = CFDictionaryCreateMutable(0, 0, NULL, &kCFTypeDictionaryValueCallBacks);
gUserAssertionTypesDict = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
assertion_types_arr[kHighPerfIndex] = kIOPMAssertionTypeNeedsCPU;
assertion_types_arr[kPreventIdleIndex] = kIOPMAssertionTypePreventUserIdleSystemSleep;
assertion_types_arr[kPreventSleepIndex] = kIOPMAssertionTypePreventSystemSleep;
assertion_types_arr[kDisableInflowIndex] = kIOPMAssertionTypeDisableInflow;
assertion_types_arr[kInhibitChargeIndex] = kIOPMAssertionTypeInhibitCharging;
assertion_types_arr[kDisableWarningsIndex] = kIOPMAssertionTypeDisableLowBatteryWarnings;
assertion_types_arr[kPreventDisplaySleepIndex] = kIOPMAssertionTypePreventUserIdleDisplaySleep;
assertion_types_arr[kEnableIdleIndex] = kIOPMAssertionTypeEnableIdleSleep;
assertion_types_arr[kNoRealPowerSourcesDebugIndex] = kIOPMAssertionTypeDisableRealPowerSources_Debug;
assertion_types_arr[kExternalMediaIndex] = _kIOPMAssertionTypeExternalMedia;
assertion_types_arr[kDeclareUserActivity] = kIOPMAssertionUserIsActive;
assertion_types_arr[kPushServiceTaskIndex] = kIOPMAssertionTypeApplePushServiceTask;
assertion_types_arr[kBackgroundTaskIndex] = kIOPMAssertionTypeBackgroundTask;
for (idx = 0; idx < kIOPMNumAssertionTypes; idx++)
configAssertionType(idx, true);
getDisplaySleepTimer(&gDisplaySleepTimer);
sendUserAssertionsToKernel(0);
#if TARGET_OS_EMBEDDED
gAssertionTypes[kEnableIdleIndex].handler(
&gAssertionTypes[kEnableIdleIndex], kAssertionOpRelease);
#else
SET_AGGREGATE_LEVEL(kEnableIdleIndex, 1);
gDebugFlags = kIOPMDebugEnableAssertionLogging;
logDispatch = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_event_handler(logDispatch, ^{
assertionLogger();
});
dispatch_source_set_cancel_handler(logDispatch, ^{
dispatch_release(logDispatch);
});
dispatch_source_set_timer(logDispatch, dispatch_time(DISPATCH_TIME_NOW, ASSERTION_LOG_DELAY * NSEC_PER_SEC),
DISPATCH_TIME_FOREVER, 0);
dispatch_resume(logDispatch);
#endif
return;
}