#include <syslog.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 "PrivateLib.h"
#include "PMSettings.h"
#include "SetActive.h"
#include "BatteryTimeRemaining.h"
#include "powermanagementServer.h"
#define kIOPMAppName "Power Management configd plugin"
#define kIOPMPrefsPath "com.apple.PowerManagement.xml"
#define kMaxTaskAssertions 64
#define kIOPMTaskPortKey CFSTR("task")
#define kIOPMTaskPIDKey CFSTR("pid")
#define kIOPMTaskAssertionsKey CFSTR("assertions")
#define kIOPMAssertionTimerRefKey CFSTR("AssertTimerRef")
#define kPMASLFacility "com.apple.powermanagement"
#define kPMASLPIDKey "Process"
#define kPMASLAssertionNameKey "AssertionName"
#define kPMASLNewCallerValueKey "NewValue"
#define kPMASLPreviousSystemValueKey "PreviousSystemValue"
#define kPMASLNewSystemValueKey "SystemValue"
#define kPMASLActionKey "Action"
#define kPMASLActionAssert "Assert"
#define kPMASLActionRelease "Release"
#define kPMASLActionClientDied "ClientDied"
#define kPMASLActionTimedOut "TimedOut"
enum {
kHighPerfIndex = 0,
kPreventIdleIndex = 1,
kDisableInflowIndex = 2,
kInhibitChargeIndex = 3,
kDisableWarningsIndex = 4,
kPreventDisplaySleepIndex = 5,
kEnableIdleIndex = 6,
kExternalMediaIndex = 7,
kIOPMNumAssertionTypes
};
enum {
kSBUCInflowDisable = 0,
kSBUCChargeInhibit = 1
};
static const int kMaxCountTimedOut = 5;
extern CFMachPortRef pmServerMachPort;
#define DEBUG_LOG(x...) do { \
asl_log(NULL, NULL, ASL_LEVEL_ERR, x); \
} while(false);
static void evaluateAssertions(void);
static void calculateAggregates(void);
static void publishAssertionStatus(void);
static void sendUserAssertionsToKernel(uint32_t user_assertions);
static void sendSmartBatteryCommand(uint32_t which, uint32_t level);
static int indexForAssertionName(CFStringRef assertionName);
static void logAssertionEvent(
const char *assertionActionStr,
CFDictionaryRef taskDictionary,
CFDictionaryRef assertionDictionary);
static void timeoutExpirationCallBack(CFRunLoopTimerRef timer, void *info);
static IOReturn copyAssertionForID(
mach_port_t inPort,
int inID,
CFDictionaryRef *outTask,
CFMutableArrayRef *outTaskAssertions,
CFMutableDictionaryRef *outAssertion);
static CFArrayRef copyPIDAssertionDictionaryFlattened(void);
static CFDictionaryRef copyAggregateValuesDictionary(void);
static CFArrayRef copyTimedOutAssertionsArray(void);
static CFMutableDictionaryRef gAssertionsDict = NULL;
static CFMutableArrayRef gTimedOutArray = NULL;
static bool gNotifyTimeOuts = false;
static CFRunLoopTimerRef gUpdateAssertionStatusTimer = NULL;
static int aggregate_assertions[kIOPMNumAssertionTypes];
static int last_aggregate_assertions[kIOPMNumAssertionTypes];
static CFStringRef assertion_types_arr[kIOPMNumAssertionTypes];
static bool idle_enable_assumed = true;
static void logAssertionEvent(
const char *assertionActionCStr,
CFDictionaryRef taskDictionary,
CFDictionaryRef assertionDictionary)
{
const int kLongStringLen = 100;
const int kShortStringLen = 10;
aslmsg m;
int aslLogLevel = ASL_LEVEL_INFO;
CFNumberRef pidNum = NULL;
int app_pid = -1;
int index = 0;
CFNumberRef levelNum = NULL;
CFStringRef assertionTypeStr = NULL;
uint32_t assertionLevel = -1;
char assertionTypeCString[kLongStringLen];
char pid_buf[kShortStringLen];
char level_buf[kShortStringLen];
char prior_system_level_buf[kShortStringLen];
char new_system_level_buf[kShortStringLen];
m = asl_new(ASL_TYPE_MSG);
asl_set(m, kPMASLActionKey, assertionActionCStr);
asl_set(m, ASL_KEY_FACILITY, kPMASLFacility);
pid_buf[0] = 0;
if (taskDictionary) {
pidNum = CFDictionaryGetValue(taskDictionary, kIOPMTaskPIDKey);
}
if (!pidNum && assertionDictionary) {
pidNum = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionPIDKey);
}
if (pidNum)
{
if (CFNumberGetValue(pidNum, kCFNumberIntType, &app_pid))
{
if (0 < snprintf(pid_buf, kShortStringLen, "%d", app_pid)) {
asl_set(m, kPMASLPIDKey, pid_buf);
}
}
}
if (isA_CFDictionary(assertionDictionary)) {
assertionTypeStr = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionTypeKey);
levelNum = CFDictionaryGetValue(assertionDictionary, kIOPMAssertionLevelKey);
CFNumberGetValue(levelNum, kCFNumberIntType, &assertionLevel);
}
if (assertionTypeStr)
{
if (CFStringGetCString(assertionTypeStr,
assertionTypeCString,
kLongStringLen,
kCFStringEncodingMacRoman))
{
asl_set(m, kPMASLAssertionNameKey, assertionTypeCString);
}
index = indexForAssertionName(assertionTypeStr);
level_buf[0] = 0;
if (0 < snprintf(level_buf, kShortStringLen, "%d", assertionLevel)) {
asl_set(m, kPMASLNewCallerValueKey, level_buf);
}
prior_system_level_buf[0] = 0;
if (0 < snprintf(prior_system_level_buf, kShortStringLen, "%d", last_aggregate_assertions[index])) {
asl_set(m, kPMASLPreviousSystemValueKey, prior_system_level_buf);
}
new_system_level_buf[0] = 0;
if (0 < snprintf(new_system_level_buf, kShortStringLen, "%d", aggregate_assertions[index])) {
asl_set(m, kPMASLNewSystemValueKey, new_system_level_buf);
}
}
if (!strncmp(assertionActionCStr, kPMASLActionTimedOut, strlen(kPMASLActionTimedOut)))
{
aslLogLevel = ASL_LEVEL_ERR;
}
asl_log(NULL, m, aslLogLevel, "PMAssertion(%s) %s %s",
pid_buf,
assertionActionCStr,
assertionTypeStr ? assertionTypeCString:"");
asl_free(m);
}
static int indexForAssertionName(CFStringRef assertionName)
{
if (CFEqual(assertionName, kIOPMAssertionTypeNeedsCPU))
return kHighPerfIndex;
else if (CFEqual(assertionName, kIOPMAssertionTypeNoIdleSleep))
return kPreventIdleIndex;
else if (CFEqual(assertionName, kIOPMAssertionTypeEnableIdleSleep))
return kEnableIdleIndex;
else if (CFEqual(assertionName, kIOPMAssertionTypeDisableInflow))
return kDisableInflowIndex;
else if (CFEqual(assertionName, kIOPMAssertionTypeInhibitCharging))
return kInhibitChargeIndex;
else if (CFEqual(assertionName, kIOPMAssertionTypeDisableLowBatteryWarnings))
return kDisableWarningsIndex;
else if (CFEqual(assertionName, kIOPMAssertionTypeNoDisplaySleep))
return kPreventDisplaySleepIndex;
else
return 0;
}
static void
calculateAggregates(void)
{
CFDictionaryRef *process_assertions = NULL;
int process_count = 0;
int i, j;
bzero( aggregate_assertions, sizeof(aggregate_assertions) );
aggregate_assertions[kEnableIdleIndex] = idle_enable_assumed;
if (!gAssertionsDict) {
goto exit;
}
process_count = CFDictionaryGetCount(gAssertionsDict);
if (0 == process_count) {
goto exit;
}
process_assertions = malloc(sizeof(CFDictionaryRef) * process_count);
CFDictionaryGetKeysAndValues(gAssertionsDict, NULL, (const void **)process_assertions);
for (i=0; i<process_count; i++)
{
CFArrayRef asst_arr = NULL;
int asst_arr_count = 0;
if(!isA_CFDictionary(process_assertions[i])) continue;
asst_arr = isA_CFArray(
CFDictionaryGetValue(process_assertions[i], kIOPMTaskAssertionsKey));
if(!asst_arr)
continue;
asst_arr_count = CFArrayGetCount(asst_arr);
for(j=0; j<asst_arr_count; j++)
{
CFDictionaryRef this_assertion = NULL;
CFStringRef asst_type = NULL;
CFNumberRef asst_val = NULL;
int val = -1;
this_assertion = CFArrayGetValueAtIndex(asst_arr, j);
if(isA_CFDictionary(this_assertion))
{
asst_type = isA_CFString(
CFDictionaryGetValue(this_assertion, kIOPMAssertionTypeKey));
asst_val = isA_CFNumber(
CFDictionaryGetValue(this_assertion, kIOPMAssertionLevelKey));
if(asst_type && asst_val) {
CFNumberGetValue(asst_val, kCFNumberIntType, &val);
if(kIOPMAssertionLevelOn == val)
{
if (CFEqual(asst_type, kIOPMAssertionTypeNeedsCPU))
{
aggregate_assertions[kHighPerfIndex] = 1;
} else if (CFEqual(asst_type, kIOPMAssertionTypeNoIdleSleep))
{
aggregate_assertions[kPreventIdleIndex] = 1;
} else if (CFEqual(asst_type, kIOPMAssertionTypeEnableIdleSleep))
{
aggregate_assertions[kEnableIdleIndex] = 1;
idle_enable_assumed = false;
} else if (CFEqual(asst_type, kIOPMAssertionTypeDisableInflow))
{
aggregate_assertions[kDisableInflowIndex] = 1;
} else if (CFEqual(asst_type, kIOPMAssertionTypeInhibitCharging))
{
aggregate_assertions[kInhibitChargeIndex] = 1;
} else if (CFEqual(asst_type, kIOPMAssertionTypeDisableLowBatteryWarnings))
{
aggregate_assertions[kDisableWarningsIndex] = 1;
} else if (CFEqual(asst_type, kIOPMAssertionTypeNoDisplaySleep))
{
aggregate_assertions[kPreventDisplaySleepIndex] = 1;
} else if (CFEqual(asst_type, _kIOPMAssertionTypeExternalMedia))
{
aggregate_assertions[kExternalMediaIndex] = 1;
}
}
}
}
}
}
free(process_assertions);
exit:
return;
}
static void
publishAssertionStatus(void)
{
SCDynamicStoreRef sharedDSRef = NULL;
CFArrayRef publishPIDToAssertionsArray = NULL;
CFDictionaryRef publishAggregateValuesDictionary = NULL;
CFArrayRef publishTimedOutArray = NULL;
static CFStringRef pidToAssertionsSCKey = NULL;
static CFStringRef aggregateValuesSCKey = NULL;
static CFStringRef timedOutArraySCKey = NULL;
static CFMutableDictionaryRef keysToSet = NULL;
static CFMutableArrayRef keysToRemove = NULL;
sharedDSRef = _getSharedPMDynamicStore();
if (!keysToSet)
{
keysToSet = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
}
if (!keysToRemove)
{
keysToRemove = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
}
if (!sharedDSRef || !keysToSet || !keysToRemove)
goto exit;
if (!pidToAssertionsSCKey)
{
pidToAssertionsSCKey = IOPMAssertionCreatePIDMappingKey();
}
if (pidToAssertionsSCKey)
{
publishPIDToAssertionsArray = copyPIDAssertionDictionaryFlattened();
if (publishPIDToAssertionsArray)
{
CFDictionarySetValue(keysToSet, pidToAssertionsSCKey, publishPIDToAssertionsArray);
CFRelease(publishPIDToAssertionsArray);
} else {
CFArrayAppendValue(keysToRemove, pidToAssertionsSCKey);
}
}
if (!aggregateValuesSCKey)
{
aggregateValuesSCKey = IOPMAssertionCreateAggregateAssertionKey();
}
if (aggregateValuesSCKey)
{
publishAggregateValuesDictionary = copyAggregateValuesDictionary();
if (publishAggregateValuesDictionary)
{
CFDictionarySetValue(keysToSet, aggregateValuesSCKey, publishAggregateValuesDictionary);
CFRelease(publishAggregateValuesDictionary);
} else {
CFArrayAppendValue(keysToRemove, aggregateValuesSCKey);
}
}
if (!timedOutArraySCKey)
{
timedOutArraySCKey = IOPMAssertionCreateTimeOutKey();
}
if (timedOutArraySCKey)
{
publishTimedOutArray = copyTimedOutAssertionsArray();
if (publishTimedOutArray)
{
CFDictionarySetValue(keysToSet, timedOutArraySCKey, publishTimedOutArray);
CFRelease(publishTimedOutArray);
}else {
CFArrayAppendValue(keysToRemove, timedOutArraySCKey);
}
}
SCDynamicStoreSetMultiple(sharedDSRef, keysToSet, keysToRemove, NULL);
CFDictionaryRemoveAllValues(keysToSet);
CFArrayRemoveAllValues(keysToRemove);
exit:
return;
}
static void
updateAssertionStatusCallback(CFRunLoopTimerRef timer, void *info)
{
publishAssertionStatus();
uint32_t status;
status = notify_post( kIOPMAssertionsChangedNotifyString );
if (gNotifyTimeOuts)
{
gNotifyTimeOuts = false;
notify_post( kIOPMAssertionTimedOutNotifyString );
}
}
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;
rootDomainService = getRootDomain();
if (IO_OBJECT_NULL == rootDomainService) {
goto exit;
}
kr = IOServiceOpen(rootDomainService, mach_task_self(), 0, &gRootDomainConnect);
if (KERN_SUCCESS != kr) {
goto exit;
}
IOConnectCallMethod(gRootDomainConnect, kPMSetUserAssertionLevels,
&in, 1,
NULL, 0, NULL,
NULL, NULL, NULL);
exit:
if (IO_OBJECT_NULL != gRootDomainConnect)
IOServiceClose(gRootDomainConnect);
return;
}
static void
evaluateAssertions(void)
{
calculateAggregates();
overrideSetting( kPMForceHighSpeed, aggregate_assertions[kHighPerfIndex]);
overrideSetting( kPMPreventDisplaySleep, aggregate_assertions[kPreventDisplaySleepIndex]);
if( aggregate_assertions[kPreventIdleIndex]
|| !aggregate_assertions[kEnableIdleIndex]) {
overrideSetting(kPMPreventIdleSleep, 1);
} else {
overrideSetting(kPMPreventIdleSleep, 0);
}
if( aggregate_assertions[kDisableInflowIndex]
!= last_aggregate_assertions[kDisableInflowIndex])
{
sendSmartBatteryCommand( kSBUCInflowDisable,
aggregate_assertions[kDisableInflowIndex]);
}
if( aggregate_assertions[kInhibitChargeIndex]
!= last_aggregate_assertions[kInhibitChargeIndex])
{
sendSmartBatteryCommand( kSBUCChargeInhibit,
aggregate_assertions[kInhibitChargeIndex]);
}
if( aggregate_assertions[kDisableWarningsIndex]
!= last_aggregate_assertions[kDisableWarningsIndex])
{
_setRootDomainProperty( CFSTR("BatteryWarningsDisabled"),
(aggregate_assertions[kDisableWarningsIndex]
? kCFBooleanTrue : kCFBooleanFalse));
}
if( aggregate_assertions[kExternalMediaIndex]
!= last_aggregate_assertions[kExternalMediaIndex])
{
sendUserAssertionsToKernel( aggregate_assertions[kExternalMediaIndex]
? kIOPMDriverAssertionExternalMediaMountedBit : 0);
}
bcopy(aggregate_assertions, last_aggregate_assertions,
sizeof(aggregate_assertions));
activateSettingOverrides();
CFAbsoluteTime fireTime = (CFAbsoluteTimeGetCurrent() + 5.0);
if (gUpdateAssertionStatusTimer)
{
CFRunLoopTimerSetNextFireDate(gUpdateAssertionStatusTimer, fireTime);
}
else
{
static const CFAbsoluteTime intervalNever = 100. * 365. * 24. * 60. * 60.;
gUpdateAssertionStatusTimer = CFRunLoopTimerCreate(0, fireTime, intervalNever, 0,
0, updateAssertionStatusCallback, 0);
if (gUpdateAssertionStatusTimer)
{
CFRunLoopAddTimer(CFRunLoopGetCurrent(), gUpdateAssertionStatusTimer, kCFRunLoopDefaultMode);
}
}
}
#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;
}
kret = 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, gid) &&
!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;
}
#define ID_FROM_INDEX(idx) (idx + 300)
#define INDEX_FROM_ID(id) (id - 300)
__private_extern__ void
PMAssertions_prime(void)
{
assertion_types_arr[kHighPerfIndex] = kIOPMAssertionTypeNeedsCPU;
assertion_types_arr[kPreventIdleIndex] = kIOPMAssertionTypeNoIdleSleep;
assertion_types_arr[kDisableInflowIndex] = kIOPMAssertionTypeDisableInflow;
assertion_types_arr[kInhibitChargeIndex] = kIOPMAssertionTypeInhibitCharging;
assertion_types_arr[kDisableWarningsIndex] = kIOPMAssertionTypeDisableLowBatteryWarnings;
assertion_types_arr[kPreventDisplaySleepIndex] = kIOPMAssertionTypeNoDisplaySleep;
assertion_types_arr[kEnableIdleIndex] = kIOPMAssertionTypeEnableIdleSleep;
assertion_types_arr[kExternalMediaIndex] = _kIOPMAssertionTypeExternalMedia;
evaluateAssertions();
return;
}
__private_extern__ void InternalAssertionCreate(
char *nameCStr,
char *assertionCStr,
int *assertion_id)
{
_IOPMAssertionCreateRequiresRoot(mach_task_self(), nameCStr, assertionCStr, kIOPMAssertionLevelOn, assertion_id);
}
__private_extern__ void InternalAssertionRelease(
int assertion_id
)
{
int status_dont_care = 0;
_io_pm_assertion_release(MACH_PORT_NULL, mach_task_self(), assertion_id, &status_dont_care);
}
IOReturn _IOPMAssertionCreateRequiresRoot
(
mach_port_t task_port,
char *nameCStr,
char *assertionCStr,
int level,
int *assertion_id
)
{
IOReturn result = kIOReturnInternalError;
CFMutableDictionaryRef this_task = NULL;
CFMutableArrayRef assertions = NULL;
CFStringRef assertionString = NULL;
CFStringRef nameString = NULL;
int task_pid_int = -1;
CFNumberRef task_pid_num = NULL;
mach_port_t oldNotify = MACH_PORT_NULL;
kern_return_t err = KERN_SUCCESS;
const mach_port_t mach_task_me = mach_task_self();
if (mach_task_me != task_port) {
__MACH_PORT_DEBUG(true, "assertion_create: expect send right", task_port);
}
*assertion_id = kIOPMNullAssertionID;
if (KERN_SUCCESS == pid_for_task(task_port, &task_pid_int)) {
task_pid_num = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &task_pid_int);
}
if (gAssertionsDict) {
this_task = (CFMutableDictionaryRef)CFDictionaryGetValue(gAssertionsDict, MY_CAST_INT_POINTER(task_port));
}
if (this_task)
{
if (mach_task_me != task_port) {
__MACH_PORT_DEBUG(true, "assertion_create: deallocate extra send right", task_port);
mach_port_deallocate(mach_task_self(), task_port);
}
} else {
if (mach_task_me != task_port) {
__MACH_PORT_DEBUG(true, "assertion_create: did set invalid callback", task_port);
}
this_task = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
if (!this_task){
result = kIOReturnNoMemory;
goto exit;
}
if (task_pid_num) {
CFDictionarySetValue(this_task, kIOPMTaskPIDKey, task_pid_num);
}
if (mach_task_me != task_port) {
err = mach_port_request_notification(
mach_task_self(), task_port, MACH_NOTIFY_DEAD_NAME, 1, CFMachPortGetPort(pmServerMachPort), MACH_MSG_TYPE_MAKE_SEND_ONCE, &oldNotify);
if (KERN_SUCCESS != err)
{
syslog(LOG_ERR, "PM assertion mach port request notification error %s(0x%08x)\n",
mach_error_string(err), err);
mach_port_deallocate(mach_task_self(), task_port);
result = err;
goto exit;
}
if (oldNotify != MACH_PORT_NULL) {
mach_port_deallocate(mach_task_self(), oldNotify);
}
}
if (!gAssertionsDict) {
gAssertionsDict = CFDictionaryCreateMutable(0, 0, NULL, &kCFTypeDictionaryValueCallBacks);
}
CFDictionarySetValue(gAssertionsDict, MY_CAST_INT_POINTER(task_port), this_task);
CFRelease(this_task);
}
assertions = (CFMutableArrayRef)CFDictionaryGetValue(this_task, kIOPMTaskAssertionsKey);
if(!assertions)
{
assertions = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
CFDictionarySetValue(this_task, kIOPMTaskAssertionsKey, assertions);
CFRelease(assertions);
}
CFIndex arrayIndex;
int asst_count;
asst_count = CFArrayGetCount(assertions);
for (arrayIndex=0; arrayIndex<asst_count; arrayIndex++)
{
if (kCFBooleanFalse == CFArrayGetValueAtIndex(assertions, arrayIndex)) break;
}
if (arrayIndex >= kMaxTaskAssertions)
{
result = kIOReturnNoMemory;
goto exit;
}
CFMutableDictionaryRef new_assertion_dict = NULL;
CFNumberRef cf_assertion_val = NULL;
CFDateRef start_date = NULL;
*assertion_id = ID_FROM_INDEX(arrayIndex);
new_assertion_dict = CFDictionaryCreateMutable(0, 0,
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
assertionString = CFStringCreateWithCString(0, assertionCStr, kCFStringEncodingMacRoman);
if (assertionString) {
CFDictionarySetValue(new_assertion_dict, kIOPMAssertionTypeKey, assertionString);
CFRelease(assertionString);
}
cf_assertion_val = CFNumberCreate(0, kCFNumberIntType, &level);
if (cf_assertion_val) {
CFDictionarySetValue(new_assertion_dict, kIOPMAssertionLevelKey, cf_assertion_val);
CFRelease(cf_assertion_val);
}
nameString = CFStringCreateWithCString(0, nameCStr, kCFStringEncodingMacRoman);
if (nameString) {
CFDictionarySetValue(new_assertion_dict, kIOPMAssertionNameKey, nameString);
CFRelease(nameString);
}
start_date = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
if (start_date) {
CFDictionarySetValue(new_assertion_dict, kIOPMAssertionCreateDateKey, start_date);
CFRelease(start_date);
}
if (task_pid_num) {
CFDictionarySetValue(new_assertion_dict, kIOPMAssertionPIDKey, task_pid_num);
CFRelease(task_pid_num);
}
CFArraySetValueAtIndex(assertions, arrayIndex, new_assertion_dict);
evaluateAssertions();
logAssertionEvent(kPMASLActionAssert, this_task, new_assertion_dict);
CFRelease(new_assertion_dict);
result = kIOReturnSuccess;
exit:
return result;
}
static const int kIOMinimumTimeoutInterval = 1;
kern_return_t _io_pm_assertion_settimeout
(
mach_port_t server __unused,
mach_port_t task,
int assertion_id,
int interval,
int *return_code
)
{
CFMutableDictionaryRef timeoutAssertion = NULL;
CFNumberRef intervalNum = NULL;
CFRunLoopTimerRef timeOutTimer = NULL;
const mach_port_t mach_task_me = mach_task_self();
if (interval < kIOMinimumTimeoutInterval) {
return kIOReturnBadArgument;
}
if (mach_task_me != task) {
__MACH_PORT_DEBUG(true, "set_timeout: expect send right", task);
}
*return_code = copyAssertionForID(task,
assertion_id,
NULL,
NULL,
&timeoutAssertion);
if (kIOReturnSuccess != *return_code) {
goto exit;
}
if (!timeoutAssertion) {
*return_code = kIOReturnNotFound;
goto exit;
}
intervalNum = CFNumberCreate(0, kCFNumberIntType, &interval);
if (intervalNum) {
CFDictionarySetValue(timeoutAssertion, kIOPMAssertionTimeOutIntervalKey, intervalNum);
CFRelease(intervalNum);
}
CFDateRef assertionCreatedDate = CFDictionaryGetValue(timeoutAssertion, kIOPMAssertionCreateDateKey);
if (assertionCreatedDate)
{
CFRunLoopTimerContext timerContext =
{ 0, (void *)timeoutAssertion, NULL, NULL, NULL };
CFAbsoluteTime fireDate = CFDateGetAbsoluteTime(assertionCreatedDate);
fireDate += (CFTimeInterval)interval;
timeOutTimer = CFRunLoopTimerCreate(0, fireDate, 0.0,
0, 0,
timeoutExpirationCallBack, &timerContext);
if (timeOutTimer)
{
CFRunLoopAddTimer(CFRunLoopGetCurrent(), timeOutTimer, kCFRunLoopDefaultMode);
CFDictionarySetValue(timeoutAssertion, kIOPMAssertionTimerRefKey, timeOutTimer);
CFRelease(timeOutTimer);
}
}
*return_code = kIOReturnSuccess;
exit:
if (mach_task_me != task) {
__MACH_PORT_DEBUG(true, "set_timeout: deallocate extra send right", task);
mach_port_deallocate(mach_task_self(), task);
}
if (timeoutAssertion) {
CFRelease(timeoutAssertion);
}
return KERN_SUCCESS;
}
kern_return_t _io_pm_assertion_release
(
mach_port_t server __unused,
mach_port_t task,
int assertion_id,
int *return_code
)
{
CFDictionaryRef callerTask = NULL;
CFMutableArrayRef taskAssertions = NULL;
CFMutableDictionaryRef releaseAssertion = NULL;
CFRunLoopTimerRef timeOutTimer = NULL;
int i;
int n;
bool releaseTask;
const mach_port_t mach_task_me = mach_task_self();
if (mach_task_me != task) {
__MACH_PORT_DEBUG(true, "assertion_release", task);
}
*return_code = copyAssertionForID(task,
assertion_id,
&callerTask,
&taskAssertions,
&releaseAssertion);
if (kIOReturnSuccess != *return_code) {
goto exit;
}
timeOutTimer = (CFRunLoopTimerRef)CFDictionaryGetValue(
releaseAssertion,
kIOPMAssertionTimerRefKey);
if (timeOutTimer)
{
CFRunLoopTimerInvalidate(timeOutTimer);
}
if (CFDictionaryGetValue(releaseAssertion, kIOPMAssertionTimedOutDateKey))
{
CFDateRef dateNow = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
CFDictionarySetValue(releaseAssertion, kIOPMAssertionReleaseDateKey, dateNow);
CFRelease(dateNow);
}
CFIndex arrayIndex = INDEX_FROM_ID(assertion_id);
CFArraySetValueAtIndex(taskAssertions, arrayIndex, kCFBooleanFalse);
releaseTask = TRUE;
n = CFArrayGetCount(taskAssertions);
for (i =0; i < n; i++) {
CFTypeRef assertion;
assertion = CFArrayGetValueAtIndex(taskAssertions, i);
if (!CFEqual(assertion, kCFBooleanFalse)) {
releaseTask = FALSE;
break;
}
}
if (releaseTask) {
CFDictionaryRemoveValue(gAssertionsDict, MY_CAST_INT_POINTER(task));
if (mach_task_me != task) {
__MACH_PORT_DEBUG(true, "assertion_release: deallocating task's #1 send right", task);
mach_port_deallocate(mach_task_self(), task);
}
}
evaluateAssertions();
logAssertionEvent(kPMASLActionRelease,
callerTask,
releaseAssertion);
if (callerTask) {
CFRelease(callerTask);
}
if (taskAssertions) {
CFRelease(taskAssertions);
}
if (releaseAssertion) {
CFRelease(releaseAssertion);
}
*return_code = kIOReturnSuccess;
exit:
if (mach_task_me != task) {
__MACH_PORT_DEBUG(true, "assertion_release: EXIT deallocating send right", task);
mach_port_deallocate(mach_task_self(), task);
}
return KERN_SUCCESS;
}
static CFArrayRef copyPIDAssertionDictionaryFlattened(void)
{
CFDictionaryRef *taskDataArray = NULL;
CFMutableArrayRef returnArray = 0;
int dict_count;
int task_index;
if(!gAssertionsDict) goto exit;
dict_count = CFDictionaryGetCount(gAssertionsDict);
if(0 == dict_count) goto exit;
returnArray = CFArrayCreateMutable(0, 0, &kCFTypeArrayCallBacks);
taskDataArray = (CFDictionaryRef *)malloc(sizeof(CFDictionaryRef)*dict_count);
if (!taskDataArray)
goto exit;
CFDictionaryGetKeysAndValues(gAssertionsDict,
(const void **)NULL,
(const void **)taskDataArray);
for (task_index=0; task_index<dict_count; task_index++)
{
CFNumberRef processID = NULL;
CFArrayRef processAssertions = NULL;
CFMutableDictionaryRef perProcessDictionary = NULL;
if (!taskDataArray[task_index])
continue;
processID = CFDictionaryGetValue(
taskDataArray[task_index],
kIOPMTaskPIDKey);
processAssertions = CFDictionaryGetValue(
taskDataArray[task_index],
kIOPMTaskAssertionsKey);
if (!processID || !processAssertions)
continue;
CFMutableArrayRef newProcessAssertionsArray = CFArrayCreateMutable(
kCFAllocatorDefault,
CFArrayGetCount(processAssertions),
&kCFTypeArrayCallBacks);
int arrayCount = CFArrayGetCount(processAssertions);
int assertion_index;
for (assertion_index = 0; assertion_index<arrayCount; assertion_index++)
{
CFMutableDictionaryRef assertionAtIndex =
(CFMutableDictionaryRef)CFArrayGetValueAtIndex(processAssertions, assertion_index);
CFMutableDictionaryRef assertionCopy = NULL;
if (isA_CFDictionary(assertionAtIndex))
{
assertionCopy = CFDictionaryCreateMutableCopy(
kCFAllocatorDefault,
CFDictionaryGetCount(assertionAtIndex),
assertionAtIndex);
if (assertionCopy)
{
CFDictionaryRemoveValue(assertionCopy, kIOPMAssertionTimerRefKey);
CFArrayAppendValue(newProcessAssertionsArray, assertionCopy);
CFRelease(assertionCopy);
}
}
}
perProcessDictionary = CFDictionaryCreateMutable(
kCFAllocatorDefault,
2,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(perProcessDictionary,
kIOPMAssertionPIDKey,
processID);
CFDictionarySetValue(perProcessDictionary,
CFSTR("PerTaskAssertions"),
newProcessAssertionsArray);
CFRelease(newProcessAssertionsArray);
CFArrayAppendValue(returnArray, perProcessDictionary);
CFRelease(perProcessDictionary);
}
free(taskDataArray);
exit:
return returnArray;
}
static CFDictionaryRef copyAggregateValuesDictionary(void)
{
CFDictionaryRef assertions_info = NULL;
CFNumberRef cf_agg_vals[kIOPMNumAssertionTypes];
int i;
for(i=0; i<kIOPMNumAssertionTypes; i++)
{
cf_agg_vals[i] = CFNumberCreate(
0,
kCFNumberIntType,
&aggregate_assertions[i]);
}
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;
}
__private_extern__ bool
PMAssertionsHandleDeadName(
mach_port_t dead_port
)
{
CFDictionaryRef deadTrackerDict = NULL;
CFRunLoopTimerRef timeOutTimer = NULL;
if (!gAssertionsDict) {
return false;
}
deadTrackerDict = CFDictionaryGetValue(gAssertionsDict, MY_CAST_INT_POINTER(dead_port));
if (!deadTrackerDict) {
return false;
}
__MACH_PORT_DEBUG(true, "cleanupAssertions: cleaning up", dead_port);
CFRetain(deadTrackerDict);
CFArrayRef dead_task_assertions
= CFDictionaryGetValue(deadTrackerDict, kIOPMTaskAssertionsKey);
if (dead_task_assertions)
{
int assertions_count = CFArrayGetCount(dead_task_assertions);
int i;
for (i=0; i<assertions_count; i++)
{
CFMutableDictionaryRef releaseAssertion = (CFMutableDictionaryRef) \
CFArrayGetValueAtIndex(dead_task_assertions, i);
if (!releaseAssertion || !isA_CFDictionary(releaseAssertion)) {
continue;
}
timeOutTimer = (CFRunLoopTimerRef)CFDictionaryGetValue(releaseAssertion,
kIOPMAssertionTimerRefKey);
if (timeOutTimer)
{
CFRunLoopTimerInvalidate(timeOutTimer);
}
if (CFDictionaryGetValue(releaseAssertion, kIOPMAssertionTimedOutDateKey))
{
CFDateRef dateNow = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
CFDictionarySetValue(releaseAssertion, kIOPMAssertionReleaseDateKey, dateNow);
CFRelease(dateNow);
}
}
}
CFDictionaryRemoveValue(gAssertionsDict, MY_CAST_INT_POINTER(dead_port));
__MACH_PORT_DEBUG(true, "cleanupAssertions: deallocating", dead_port);
mach_port_deallocate(mach_task_self(), dead_port);
evaluateAssertions();
logAssertionEvent(kPMASLActionClientDied,
deadTrackerDict,
NULL );
CFRelease(deadTrackerDict);
__MACH_PORT_DEBUG(true, "cleanupAssertions: exiting", dead_port);
return true;
}
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);
}
static void timeoutExpirationCallBack(CFRunLoopTimerRef timer, void *info)
{
CFMutableDictionaryRef assertion = (CFMutableDictionaryRef)isA_CFDictionary(info);
if (!assertion)
return;
CFDictionaryRemoveValue(assertion, kIOPMAssertionTimerRefKey);
CFDateRef dateNow = CFDateCreate(0, CFAbsoluteTimeGetCurrent());
CFDictionarySetValue(assertion, kIOPMAssertionTimedOutDateKey, dateNow);
CFRelease(dateNow);
appendTimedOutAssertion(assertion);
evaluateAssertions();
gNotifyTimeOuts = true;
}
static IOReturn copyAssertionForID(
mach_port_t inPort,
int inID,
CFDictionaryRef *outTask,
CFMutableArrayRef *outTaskAssertions,
CFMutableDictionaryRef *outAssertion)
{
CFDictionaryRef localTask = NULL;
CFMutableArrayRef localTaskAssertions = NULL;
CFMutableDictionaryRef localAssertion = NULL;
IOReturn ret;
if (outTask) {
*outTask = NULL;
}
if (outTaskAssertions) {
*outTaskAssertions = NULL;
}
if (outAssertion) {
*outAssertion = NULL;
}
if (gAssertionsDict) {
localTask = CFDictionaryGetValue(gAssertionsDict, MY_CAST_INT_POINTER(inPort));
}
if (!localTask) {
ret = kIOReturnNotFound;
goto exit;
}
localTaskAssertions = (CFMutableArrayRef)CFDictionaryGetValue(localTask, kIOPMTaskAssertionsKey);
if (!localTaskAssertions) {
ret = kIOReturnInternalError;
goto exit;
}
CFIndex arrayIndex = INDEX_FROM_ID(inID);
if ((arrayIndex < 0) || (arrayIndex >= CFArrayGetCount(localTaskAssertions))) {
ret = kIOReturnNotFound;
goto exit;
}
localAssertion = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(localTaskAssertions, arrayIndex);
if (!localAssertion
|| !isA_CFDictionary(localAssertion)) {
ret = kIOReturnNotFound;
goto exit;
}
if (outTask) {
CFRetain(localTask);
*outTask = localTask;
}
if (outTaskAssertions) {
CFRetain(localTaskAssertions);
*outTaskAssertions = localTaskAssertions;
}
if (outAssertion) {
CFRetain(localAssertion);
*outAssertion = localAssertion;
}
ret = kIOReturnSuccess;
exit:
return ret;
}