1/* 2 * Copyright (c) 2008 Apple Computer, Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 29#include "powermanagementServer.h" // mig generated 30 31#include <asl.h> 32#include <stdio.h> 33#include <stdlib.h> 34#include <unistd.h> 35#include <mach/mach.h> 36#include <mach/message.h> 37#include <notify.h> 38#include <bsm/libbsm.h> 39#include <IOKit/pwr_mgt/IOPM.h> 40#include <libproc.h> 41#include <sys/syscall.h> 42#include <Kernel/kern/debug.h> 43 44#include "PrivateLib.h" 45#include "PMConnection.h" 46#include "AutoWakeScheduler.h" 47#include "RepeatingAutoWake.h" 48#include "PMAssertions.h" 49#include "PMStore.h" 50#include "SystemLoad.h" 51#if !TARGET_OS_EMBEDDED 52#include "TTYKeepAwake.h" 53#endif 54#include "PMSettings.h" 55#include "Platform.h" 56 57/************************************************************************************/ 58 59// Bits for gPowerState 60#define kSleepState 0x01 61#define kDarkWakeState 0x02 62#define kDarkWakeForBTState 0x04 63#define kDarkWakeForSSState 0x08 64#define kDarkWakeForMntceState 0x10 65#define kDarkWakeForServerState 0x20 66#define kFullWakeState 0x40 67#define kNotificationDisplayWakeState 0x80 68#define kPowerStateMask 0xff 69 70#ifndef kIOPMMaintenanceScheduleImmediate 71#define kIOPMMaintenanceScheduleImmediate "MaintenanceImmediate" 72#endif 73 74// Number of seconds before auto power off timer we let system go to sleep 75#define kAutoPowerOffSleepAhead (0) 76 77/* Bookkeeping data types */ 78 79enum { 80 kSleepWakeInterestBit = 1, 81 kMaintenanceInterestBit = 2 82}; 83 84enum { 85 _kSleepStateBits = 0x0000, 86 _kOnStateBits = 0xFFFF 87}; 88 89/* Array indices & for PMChooseScheduledEvent */ 90typedef enum { 91 kChooseFullWake = 0, 92 kChooseMaintenance = 1, 93 kChooseSleepServiceWake = 2, 94 kChooseTimerPlugin = 3, 95 kChooseWakeTypeCount = 4 96} wakeType_e; 97 98enum { 99 kSilentRunningOff = 0, 100 kSilentRunningOn = 1 101}; 102 103/* Auto Poweroff info */ 104static dispatch_source_t gApoDispatch; 105CFAbsoluteTime ts_apo = 0; // Time at which system should go for auto power off 106 107static int const kMaxConnectionIDCount = 1000*1000*1000; 108static int const kConnectionOffset = 1000; 109static double const kPMConnectionNotifyTimeoutDefault = 28.0; 110#if !TARGET_OS_EMBEDDED 111static int kPMSleepDurationForBT = (30*60); // Defaults to 30 mins 112static int kPMDarkWakeLingerDuration = 15; // Defaults to 15 secs 113static int kPMACWakeLingerDuration = 45; // Defaults to 15 secs 114#if LOG_SLEEPSERVICES 115static int gNotifySleepServiceToken = 0; 116#endif 117// Time at which system can wake for next PowerNap 118static CFAbsoluteTime ts_nextPowerNap = 0; 119#endif 120 121 122// gPowerState - Tracks various wake types the system is currently in 123// by setting appropriate bits. 124static uint32_t gPowerState; 125 126static io_service_t rootDomainService = IO_OBJECT_NULL; 127static IOPMCapabilityBits gCurrentCapabilityBits = kIOPMCapabilityCPU | kIOPMCapabilityDisk 128 | kIOPMCapabilityNetwork | kIOPMCapabilityAudio | kIOPMCapabilityVideo; 129extern CFMachPortRef pmServerMachPort; 130 131 132/************************************************************************************/ 133/************************************************************************************/ 134/************************************************************************************/ 135 136/* Bookkeeping structs */ 137 138/* PMResponseWrangler 139 * While we have an outstanding notification, we have one of these guys sitting around 140 * waiting to handle the incoming responses. 141 * We assert that only one PMResponseWrangler shall exist at a time - e.g. no more 142 * than one system state transition shall occur simultaneously. 143 */ 144typedef struct { 145 CFMutableArrayRef awaitingResponses; 146 CFMutableArrayRef responseStats; 147 CFRunLoopTimerRef awaitingResponsesTimeout; 148 CFAbsoluteTime allRepliedTime; 149 long kernelAcknowledgementID; 150 int notificationType; 151 int awaitingResponsesCount; 152 int awaitResponsesTimeoutSeconds; 153 int completedStatus; // status after timed out or, all acked 154 bool completed; 155 bool nextIsValid; 156 long nextKernelAcknowledgementID; 157 int nextInterestBits; 158} PMResponseWrangler; 159 160 161/* PMConnection - one tracker corresponds to one PMConnection 162 * in an application. 163 * 164 * responseHandler - Should be NULL unless this connection has outstanding 165 * notifications to reply to. 166 */ 167typedef struct { 168 mach_port_t notifyPort; 169 PMResponseWrangler *responseHandler; 170 CFStringRef callerName; 171 uint32_t uniqueID; 172 int callerPID; 173 IOPMCapabilityBits interestsBits; 174 bool notifyEnable; 175 int timeoutCnt; 176} PMConnection; 177 178 179/* PMResponse 180 * represents one outstanding notification acknowledgement 181 */ 182typedef struct { 183 PMConnection *connection; 184 PMResponseWrangler *myResponseWrangler; 185 IOPMConnectionMessageToken token; 186 CFAbsoluteTime repliedWhen; 187 CFAbsoluteTime notifiedWhen; 188 CFAbsoluteTime maintenanceRequested; 189 CFAbsoluteTime timerPluginRequested; 190 CFAbsoluteTime sleepServiceRequested; 191 CFStringRef clientInfoString; 192 int sleepServiceCapTimeoutMS; 193 int notificationType; 194 bool replied; 195 bool timedout; 196} PMResponse; 197 198 199/************************************************************************************/ 200/************************************************************************************/ 201/************************************************************************************/ 202 203/* Internal methods */ 204 205 206#define VALID_DATE(x) (x!=0.0) 207/* 208 * PMScheduleWakeEventChooseBest 209 * 210 * Expected to be called ONCE at each system sleep by PMConnection.c. 211 * Compares maintenance, sleepservice, and autowake requests, and schedules the earliest event with the RTC. 212 */ 213static IOReturn createConnectionWithID( 214 PMConnection **); 215 216static PMConnection *connectionForID( 217 uint32_t findMe); 218 219static CFArrayRef createArrayOfConnectionsWithInterest( 220 int interestBitsNotify); 221 222static PMResponseWrangler *connectionFireNotification( 223 int notificationType, 224 long kernelAcknowledgementID); 225 226static void _sendMachMessage( 227 mach_port_t port, 228 mach_msg_id_t msg_id, 229 uint32_t payload_bits, 230 uint32_t payload_messagetoken); 231 232static void checkResponses(PMResponseWrangler *wrangler); 233 234static void PMScheduleWakeEventChooseBest(CFAbsoluteTime scheduleTime, wakeType_e type); 235 236static void responsesTimedOut(CFRunLoopTimerRef timer, void * info); 237 238static void cleanupConnection(PMConnection *reap); 239 240static void cleanupResponseWrangler(PMResponseWrangler *reap); 241 242static void setSystemSleepStateTracking(IOPMCapabilityBits); 243 244#if !TARGET_OS_EMBEDDED 245static void scheduleSleepServiceCapTimerEnforcer(uint32_t cap_ms); 246#endif 247 248/* Hide SleepServices code for public OS seeds. 249 * We plan to re-enable this code for shipment. 250 * ETB 1/24/12 251 */ 252 253#if LOG_SLEEPSERVICES 254#if !TARGET_OS_EMBEDDED 255static void logASLMessageSleepServiceBegins(long withCapTime); 256#endif 257__private_extern__ void logASLMessageSleepServiceTerminated(int forcedTimeoutCnt); 258#endif 259 260static void PMConnectionPowerCallBack( 261 void *port, 262 io_service_t rootdomainservice, 263 natural_t messageType, 264 void *messageData); 265 266__private_extern__ void ClockSleepWakeNotification(IOPMCapabilityBits b, 267 IOPMCapabilityBits c, 268 uint32_t changeFlags); 269 270 271void setAutoPowerOffTimer(bool initialCall, CFAbsoluteTime postpone); 272static void sendNoRespNotification( int interestBitsNotify ); 273void cancelAutoPowerOffTimer(); 274 275/************************************************************************************/ 276/************************************************************************************/ 277/************************************************************************************/ 278 279/* CFArrayRef support structures */ 280 281static Boolean _CFArrayConnectionEquality(const void *value1, const void *value2) 282{ 283 const PMConnection *v1 = (const PMConnection *)value1; 284 const PMConnection *v2 = (const PMConnection *)value2; 285 return (v1->uniqueID == v2->uniqueID); 286} 287static CFArrayCallBacks _CFArrayConnectionCallBacks = 288 { 0, NULL, NULL, NULL, _CFArrayConnectionEquality }; 289static CFArrayCallBacks _CFArrayVanillaCallBacks = 290 { 0, NULL, NULL, NULL, NULL }; 291 292/************************************************************************************/ 293/************************************************************************************/ 294/************************************************************************************/ 295 296/* Globals */ 297 298static CFMutableArrayRef gConnections = NULL; 299 300static uint32_t globalConnectionIDTally = 0; 301 302static io_connect_t gRootDomainConnect = IO_OBJECT_NULL; 303 304static PMResponseWrangler * gLastResponseWrangler = NULL; 305 306SleepServiceStruct gSleepService; 307 308uint32_t gDebugFlags = 0; 309 310uint32_t gCurrentSilentRunningState = kSilentRunningOff; 311 312#if !TARGET_OS_EMBEDDED 313static bool gForceDWL = false; 314#endif 315 316bool gMachineStateRevertible = true; 317 318/************************************************************************************/ 319/************************************************************************************/ 320/************************************************************************************/ 321 322#define IS_DARK_CAPABILITIES(x) \ 323 (BIT_IS_SET(x, kIOPMSystemCapabilityCPU) \ 324 && BIT_IS_NOT_SET(x, kIOPMSystemCapabilityGraphics|kIOPMSystemCapabilityAudio)) 325 326#define SYSTEM_WILL_WAKE(x) \ 327 ( BIT_IS_SET(capArgs->changeFlags, kIOPMSystemCapabilityWillChange) \ 328 && IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityCPU) ) 329 330#define SYSTEM_DID_WAKE(x) \ 331 ( BIT_IS_SET(capArgs->changeFlags, kIOPMSystemCapabilityDidChange) \ 332 && (IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityCPU) \ 333 || IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityGraphics) ) ) 334 335#define SYSTEM_WILL_SLEEP_TO_S0(x) \ 336 ((CAPABILITY_BIT_CHANGED(x->fromCapabilities, x->toCapabilities, kIOPMSystemCapabilityCPU)) \ 337 && BIT_IS_SET(x->changeFlags, kIOPMSystemCapabilityWillChange) \ 338 && BIT_IS_NOT_SET(x->toCapabilities, kIOPMSystemCapabilityCPU)) 339 340#define SYSTEM_WILL_SLEEP_TO_S0DARK(x) \ 341 ((CHANGED_CAP_BITS(x->fromCapabilities, x->toCapabilities) == (kIOPMSystemCapabilityGraphics|kIOPMSystemCapabilityAudio)) \ 342 && BIT_IS_SET(x->changeFlags, kIOPMSystemCapabilityWillChange) \ 343 && BIT_IS_NOT_SET(x->toCapabilities, kIOPMSystemCapabilityGraphics|kIOPMSystemCapabilityAudio)) 344/************************************************************************************/ 345/************************************************************************************/ 346/************************************************************************************/ 347 348/* 349 * PMConnection_prime 350 */ 351__private_extern__ 352void PMConnection_prime() 353{ 354 io_object_t sleepWakeCallbackHandle = IO_OBJECT_NULL; 355 IONotificationPortRef notify = NULL; 356 kern_return_t kr = 0; 357 char *errorString = NULL; 358 359 bzero(&gSleepService, sizeof(gSleepService)); 360 361 gConnections = CFArrayCreateMutable(kCFAllocatorDefault, 100, &_CFArrayConnectionCallBacks); 362 363 // Find it 364 rootDomainService = getRootDomain(); 365 if (IO_OBJECT_NULL == rootDomainService) { 366 errorString = "Could not find IOPMrootDomain"; 367 goto error; 368 } 369 370 // Open it 371 kr = IOServiceOpen(rootDomainService, mach_task_self(), 0, &gRootDomainConnect); 372 if (KERN_SUCCESS != kr) { 373 errorString = "Could not open IOPMrootDomain"; 374 goto error; 375 } 376 377 notify = IONotificationPortCreate(MACH_PORT_NULL); 378 if (!notify) { 379 errorString = "Could not create IONotificationPort"; 380 goto error; 381 } 382 383 // Register for sleep wake notifications 384 kr = IOServiceAddInterestNotification(notify, rootDomainService, "IOPMSystemCapabilityInterest", 385 (IOServiceInterestCallback) PMConnectionPowerCallBack, NULL, 386 &sleepWakeCallbackHandle); 387 if (KERN_SUCCESS != kr) { 388 errorString = "Could not add interest notification kIOPMAppPowerStateInterest"; 389 goto error; 390 } 391 392 CFRunLoopAddSource(CFRunLoopGetCurrent(), 393 IONotificationPortGetRunLoopSource(notify), 394 kCFRunLoopDefaultMode); 395 396 return; 397 398error: 399 // ASL_LOG: KEEP 400 asl_log(NULL, NULL, ASL_LEVEL_ERR, 401 "PowerManagement: unable to register with kernel power management. %s %s", 402 errorString ? "Reason = : ":"", errorString ? errorString:"unknown"); 403 return; 404} 405 406 407/*****************************************************************************/ 408/*****************************************************************************/ 409/*****************************************************************************/ 410/*****************************************************************************/ 411 412/* M I G */ 413/* M I G */ 414/* M I G */ 415/* M I G */ 416/* M I G */ 417 418#pragma mark - 419#pragma mark MIG 420 421/* 422 * Create a PM Connection 423 * - assign a globally unique ID 424 * - find the remote mach port 425 * - get an invalidation notification on the remote callback 426 * 427 */ 428kern_return_t _io_pm_connection_create 429( 430 mach_port_t server, 431 mach_port_t task_in, 432 string_t name, 433 int interests, 434 uint32_t *connection_id, 435 int *return_code 436) 437{ 438 PMConnection *newConnection = NULL; 439 int task_pid; 440 441 // Allocate: create a new PMConnection type 442 createConnectionWithID(&newConnection); 443 if (!newConnection) { 444 *return_code = kIOReturnError; 445 goto exit; 446 } 447 448 if (KERN_SUCCESS == pid_for_task(task_in, &task_pid)) { 449 newConnection->callerPID = task_pid; 450 } 451 452 // Save caller name for logging 453 if (name && strlen(name)) { 454 newConnection->callerName = CFStringCreateWithCString(0, name, kCFStringEncodingUTF8); 455 } 456 457 newConnection->interestsBits = interests; 458 *connection_id = newConnection->uniqueID; 459 *return_code = kIOReturnSuccess; 460 461exit: 462 463 if (MACH_PORT_NULL != task_in) 464 { 465 // Release the send right on task_in that we received as an argument 466 // to this method. 467 __MACH_PORT_DEBUG(true, "_io_pm_connection_create dropping send right", task_in); 468 mach_port_deallocate(mach_task_self(), task_in); 469 } 470 471 return KERN_SUCCESS; 472} 473 474 475/*****************************************************************************/ 476/*****************************************************************************/ 477 478 kern_return_t _io_pm_connection_schedule_notification( 479 mach_port_t server, 480 uint32_t connection_id, 481 mach_port_t notify_port_in, 482 int disable, 483 int *return_code) 484{ 485 PMConnection *connection = NULL; 486 mach_port_t oldNotify; 487 488 if (MACH_PORT_NULL == notify_port_in || NULL == return_code) { 489 if (return_code) *return_code = kIOReturnBadArgument; 490 goto exit; 491 } 492 493 __MACH_PORT_DEBUG(true, "_io_pm_connection_schedule_notification notify_port", notify_port_in); 494 495 *return_code = kIOReturnError; 496 497 connection = connectionForID(connection_id); 498 if (!connection) { 499 return kIOReturnNotFound; 500 } 501 502 connection->notifyEnable = (disable == 0); 503 504 if (!disable && (MACH_PORT_NULL == connection->notifyPort)) { 505 connection->notifyPort = notify_port_in; 506 507 mach_port_request_notification( 508 mach_task_self(), // task 509 notify_port_in, // port that will die 510 MACH_NOTIFY_DEAD_NAME, // msgid 511 1, // make-send count 512 CFMachPortGetPort(pmServerMachPort), // notify port 513 MACH_MSG_TYPE_MAKE_SEND_ONCE, // notifyPoly 514 &oldNotify); // previous 515 516 __MACH_PORT_DEBUG(true, "Registered dead name notification on notifyPort", notify_port_in); 517 } else { 518 mach_port_deallocate(mach_task_self(), notify_port_in); 519 } 520 521 *return_code = kIOReturnSuccess; 522exit: 523 return KERN_SUCCESS; 524} 525 526/*****************************************************************************/ 527/*****************************************************************************/ 528 529kern_return_t _io_pm_connection_release 530( 531 mach_port_t server, 532 uint32_t connection_id, 533 int *return_code 534) 535{ 536 PMConnection *cleanMeUp = NULL; 537 538 cleanMeUp = connectionForID(connection_id); 539 540 if (cleanMeUp) { 541 cleanupConnection(cleanMeUp); 542 if (return_code) 543 *return_code = kIOReturnSuccess; 544 } else { 545 if (return_code) 546 *return_code = kIOReturnNotFound; 547 } 548 549 return KERN_SUCCESS; 550} 551 552/*****************************************************************************/ 553/*****************************************************************************/ 554 555/* 556 * _io_pm_acknowledge_event_findOutstandingResponseForToken 557 * Helper function to improve readability of connection_acknowledge_event 558 */ 559 560#if !TARGET_OS_EMBEDDED 561static PMResponse *_io_pm_acknowledge_event_findOutstandingResponseForToken(PMConnection *connection, int token) 562{ 563 CFMutableArrayRef responsesTrackingList = NULL; 564 int responsesCount = 0; 565 PMResponse *checkResponse = NULL; 566 PMResponse *foundResponse = NULL; 567 int i; 568 569 570 if (!connection 571 || !connection->responseHandler 572 || !(responsesTrackingList = connection->responseHandler->awaitingResponses)) 573 { 574 return NULL; 575 } 576 577 responsesCount = CFArrayGetCount(responsesTrackingList); 578 579 for (i=0; i<responsesCount; i++) 580 { 581 checkResponse = (PMResponse *)CFArrayGetValueAtIndex(responsesTrackingList, i); 582 if (checkResponse && (token == checkResponse->token)) { 583 foundResponse = checkResponse; 584 break; 585 } 586 } 587 588 return foundResponse; 589} 590 591/* 592 * _io_pm_connection_acknowledge_event_unpack_payload 593 * Helper function to improve readability of connection_acknowledge_event 594 */ 595static CFDictionaryRef _io_pm_connection_acknowledge_event_unpack_payload( 596 vm_offset_t ptr, 597 mach_msg_type_number_t len) 598{ 599 CFDataRef optionsAsData = NULL; 600 CFPropertyListRef optionsUnzipped = NULL; 601 CFDictionaryRef ackOptionsDict = NULL; 602 603 if ( ptr != 0 && len != 0) 604 { 605 optionsAsData = CFDataCreateWithBytesNoCopy(0, (const uint8_t *)ptr, len, kCFAllocatorNull); 606 if (optionsAsData) 607 { 608 optionsUnzipped = CFPropertyListCreateWithData(0, optionsAsData, kCFPropertyListImmutable, NULL, NULL); 609 ackOptionsDict = (CFDictionaryRef) isA_CFDictionary(optionsUnzipped); 610 if (optionsUnzipped && !ackOptionsDict) { 611 CFRelease(optionsUnzipped); 612 } 613 CFRelease(optionsAsData); 614 } 615 } 616 617 return ackOptionsDict; 618} 619#endif 620 621static void cacheResponseStats(PMResponse *resp) 622{ 623 PMResponseWrangler *respWrangler = resp->myResponseWrangler; 624 CFMutableDictionaryRef stats = NULL; 625 uint32_t timeIntervalMS; 626 CFStringRef respType = NULL; 627 628 if (respWrangler->responseStats == NULL) 629 return; 630 631 timeIntervalMS = (resp->repliedWhen - resp->notifiedWhen) * 1000; 632 633 if (resp->timedout) { 634 respType = CFSTR(kIOPMStatsResponseTimedOut); 635 } 636 else if (timeIntervalMS > kAppResponseLogThresholdMS) { 637 respType = CFSTR(kIOPMStatsResponseSlow); 638 } 639 else if (gDebugFlags & kIOPMDebugLogCallbacks) { 640 respType = CFSTR(kIOPMStatsResponsePrompt); 641 } 642 else return; 643 644 stats = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 645 if (stats == NULL) 646 return; 647 648 CFDictionarySetValue(stats, CFSTR(kIOPMStatsApplicationResponseTypeKey), respType); 649 CFDictionarySetValue(stats, CFSTR(kIOPMStatsNameKey), resp->connection->callerName); 650 651 CFNumberRef capsNum = CFNumberCreate(NULL, kCFNumberIntType, &resp->notificationType); 652 if (capsNum) { 653 CFDictionarySetValue(stats, CFSTR(kIOPMStatsPowerCapabilityKey), capsNum); 654 CFRelease(capsNum); 655 } 656 657 CFNumberRef timeIntervalNumber = CFNumberCreate(NULL, kCFNumberIntType, &timeIntervalMS); 658 if (timeIntervalNumber) { 659 CFDictionarySetValue(stats, CFSTR(kIOPMStatsTimeMSKey), timeIntervalNumber); 660 CFRelease(timeIntervalNumber); 661 } 662 663 if (gPowerState & kSleepState) { 664 CFDictionarySetValue(stats, CFSTR(kIOPMStatsSystemTransitionKey), CFSTR("Sleep")); 665 } 666 else if (gPowerState & kDarkWakeState) { 667 CFDictionarySetValue(stats, CFSTR(kIOPMStatsSystemTransitionKey), CFSTR("DarkWake")); 668 } 669 else if (gPowerState & kFullWakeState) { 670 CFDictionarySetValue(stats, CFSTR(kIOPMStatsSystemTransitionKey), CFSTR("Wake")); 671 } 672 673 CFArrayAppendValue(respWrangler->responseStats, stats); 674 675 CFRelease(stats); 676} 677 678kern_return_t _io_pm_connection_acknowledge_event 679( 680 mach_port_t server, 681 uint32_t connection_id, 682 int messageToken, 683 vm_offset_t options_ptr, 684 mach_msg_type_number_t options_len, 685 int *return_code 686 ) 687{ 688#if TARGET_OS_EMBEDDED 689 return KERN_FAILURE; 690#else 691 PMConnection *connection = connectionForID(connection_id); 692 PMResponse *foundResponse = NULL; 693 694 // Check options dictionary 695 CFDictionaryRef ackOptionsDict = NULL; 696 CFDateRef requestDate = NULL; 697 698 if (messageToken == 0) 699 { 700 /* 701 * This response is for a message for which response is not expected. 702 * There is no record mainatained for these messages. 703 */ 704 *return_code = kIOReturnSuccess; 705 goto exit; 706 } 707 708 if (!(foundResponse = _io_pm_acknowledge_event_findOutstandingResponseForToken(connection, messageToken))) { 709 *return_code = kIOReturnNotFound; 710 goto exit; 711 } 712 713 *return_code = kIOReturnSuccess; 714 foundResponse->repliedWhen = CFAbsoluteTimeGetCurrent(); 715 foundResponse->replied = true; 716 717 cacheResponseStats(foundResponse); 718 719 // Unpack the passed-in options data structure 720 if ((ackOptionsDict = _io_pm_connection_acknowledge_event_unpack_payload(options_ptr, options_len))) 721 { 722 723 foundResponse->clientInfoString = CFDictionaryGetValue(ackOptionsDict, kIOPMAckClientInfoKey); 724 if (foundResponse->clientInfoString) { 725 CFRetain(foundResponse->clientInfoString); 726 } 727 728 /* 729 * Caller requests a maintenance wake. These schedule on AC only. 730 * 731 * kIOPMAckWakeDate 732 * kIOPMAckNetworkMaintenanceWakeDate 733 * kIOPMAckSUWakeDate 734 */ 735 requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckWakeDate)); 736 if (!requestDate) { 737 requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckNetworkMaintenanceWakeDate)); 738 } 739 740 if (!requestDate) { 741 requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckSUWakeDate)); 742 } 743 744 if (requestDate 745 && (kACPowered == _getPowerSource())) 746 { 747 foundResponse->maintenanceRequested = CFDateGetAbsoluteTime(requestDate); 748 } 749 750 /* kIOPMAckBackgroundTaskWakeDate 751 * - Timer plugin uses this to schedule DWBT timed work. 752 * - Schedules on AC only 753 * - Schedules on SilentRunning machines only 754 */ 755 requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckBackgroundTaskWakeDate)); 756 757 if (requestDate && _DWBT_allowed()) 758 { 759 foundResponse->timerPluginRequested = CFDateGetAbsoluteTime(requestDate); 760#if !TARGET_OS_EMBEDDED 761 if ( foundResponse->timerPluginRequested < ts_nextPowerNap ) 762 foundResponse->timerPluginRequested = ts_nextPowerNap; 763#endif 764 } 765 766 /* kIOPMAckAppRefreshWakeDate 767 * - CTS uses this to schedule SleepService work. 768 * - Schedules on AC & Battery 769 * - Schedules on SilentRunning/PowerNap supported machines only 770 */ 771 requestDate = isA_CFDate(CFDictionaryGetValue(ackOptionsDict, kIOPMAckAppRefreshWakeDate)); 772 773 if (requestDate && _SS_allowed()) 774 { 775 foundResponse->sleepServiceRequested = CFDateGetAbsoluteTime(requestDate); 776#if !TARGET_OS_EMBEDDED 777 if ( foundResponse->sleepServiceRequested < ts_nextPowerNap ) 778 foundResponse->sleepServiceRequested = ts_nextPowerNap; 779#endif 780 } 781 782 CFRelease(ackOptionsDict); 783 } 784 785 checkResponses(connection->responseHandler); 786 787 788exit: 789 vm_deallocate(mach_task_self(), options_ptr, options_len); 790 791 return KERN_SUCCESS; 792#endif 793} 794 795kern_return_t _io_pm_get_capability_bits( 796 mach_port_t server, 797 audit_token_t token, 798 uint32_t *capBits, 799 int *return_code ) 800{ 801 802 *capBits = gCurrentCapabilityBits; 803 *return_code = kIOReturnSuccess; 804 return KERN_SUCCESS; 805} 806 807/*****************************************************************************/ 808/*****************************************************************************/ 809 810kern_return_t _io_pm_connection_copy_status 811( 812 mach_port_t server, 813 int status_index, 814 vm_offset_t *status_data, 815 mach_msg_type_number_t *status_dataCnt, 816 int *return_val 817) 818{ 819 return KERN_SUCCESS; 820} 821 822kern_return_t _io_pm_set_debug_flags( 823 mach_port_t server, 824 audit_token_t token, 825 uint32_t newFlags, 826 uint32_t setMode, 827 uint32_t *oldFlags, 828 int *return_code ) 829{ 830 pid_t callerPID = -1; 831 uid_t callerUID = -1; 832 gid_t callerGID = -1; 833 834 audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, &callerGID, &callerPID, NULL, NULL); 835 if ( !(callerIsRoot(callerUID) || callerIsAdmin(callerUID, callerGID) )) 836 { 837 *return_code = kIOReturnNotPrivileged; 838 goto exit; 839 } 840 841 if (oldFlags) 842 *oldFlags = gDebugFlags; 843 844 switch (setMode) { 845 case kIOPMDebugFlagsSetBits: 846 gDebugFlags |= newFlags; 847 break; 848 849 case kIOPMDebugFlagsResetBits: 850 gDebugFlags &= ~newFlags; 851 break; 852 853 case kIOPMDebugFlagsSetValue: 854 gDebugFlags = newFlags; 855 break; 856 857 default: 858 break; 859 } 860 861 *return_code = kIOReturnSuccess; 862 863exit: 864 865 866 return KERN_SUCCESS; 867} 868 869kern_return_t _io_pm_set_bt_wake_interval 870( 871 mach_port_t server, 872 audit_token_t token, 873 uint32_t newInterval, 874 uint32_t *oldInterval, 875 int *return_code 876) 877{ 878#if !TARGET_OS_EMBEDDED 879 pid_t callerPID = -1; 880 uid_t callerUID = -1; 881 gid_t callerGID = -1; 882 883 audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, &callerGID, &callerPID, NULL, NULL); 884 if ( !(callerIsRoot(callerUID) || callerIsAdmin(callerUID, callerGID) )) 885 { 886 *return_code = kIOReturnNotPrivileged; 887 goto exit; 888 } 889 890 if (oldInterval) 891 *oldInterval = kPMSleepDurationForBT; 892 kPMSleepDurationForBT = newInterval; 893#endif 894 895 *return_code = kIOReturnSuccess; 896 897#if !TARGET_OS_EMBEDDED 898exit: 899#endif 900 return KERN_SUCCESS; 901} 902 903 904kern_return_t _io_pm_set_dw_linger_interval 905( 906 mach_port_t server, 907 audit_token_t token, 908 uint32_t newInterval, 909 uint32_t *oldInterval, 910 int *return_code 911) 912{ 913#if !TARGET_OS_EMBEDDED 914 pid_t callerPID = -1; 915 uid_t callerUID = -1; 916 gid_t callerGID = -1; 917 918 audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, &callerGID, &callerPID, NULL, NULL); 919 if ( !(callerIsRoot(callerUID) || callerIsAdmin(callerUID, callerGID) )) 920 { 921 *return_code = kIOReturnNotPrivileged; 922 goto exit; 923 } 924 925 if (oldInterval) 926 *oldInterval = kPMDarkWakeLingerDuration; 927 kPMDarkWakeLingerDuration = newInterval; 928 // Force fake sleep even on unsupported platforms 929 gForceDWL = true; 930#endif 931 932 *return_code = kIOReturnSuccess; 933 934#if !TARGET_OS_EMBEDDED 935exit: 936#endif 937 return KERN_SUCCESS; 938} 939 940/************************************************************************************/ 941/************************************************************************************/ 942/************************************************************************************/ 943/************************************************************************************/ 944/************************************************************************************/ 945 946#pragma mark - 947#pragma mark Connection 948 949static void cleanupConnection(PMConnection *reap) 950{ 951 PMResponseWrangler *responseWrangler = NULL; 952 PMResponse *openResponse = NULL; 953 CFIndex i, allResponsesCount = 0; 954 CFIndex index; 955 CFRange connectionsRange = 956 CFRangeMake(0, CFArrayGetCount(gConnections)); 957 958 if (MACH_PORT_NULL != reap->notifyPort) 959 { 960 // Release the send right on reap->notifyPort that we obtained 961 // when we received it as an argument to _io_pm_connection_schedule_notification. 962 __MACH_PORT_DEBUG(true, "IOPMConnection cleanupConnection drop notifyPort", reap->notifyPort); 963 mach_port_deallocate(mach_task_self(), reap->notifyPort); 964 reap->notifyPort = MACH_PORT_NULL; 965 } 966 967 if (reap->callerName) { 968 CFRelease(reap->callerName); 969 reap->callerName = NULL; 970 } 971 972 responseWrangler = reap->responseHandler; 973 if (responseWrangler && responseWrangler->awaitingResponses) 974 { 975 allResponsesCount = CFArrayGetCount(responseWrangler->awaitingResponses); 976 977 for (i=0; i<allResponsesCount; i++) 978 { 979 openResponse = (PMResponse *)CFArrayGetValueAtIndex( 980 responseWrangler->awaitingResponses, i); 981 982 if (openResponse && (openResponse->connection == reap)) { 983 openResponse->connection = NULL; 984 openResponse->replied = true; 985 openResponse->timedout = true; 986 break; 987 } 988 } 989 990 // Let the response wrangler finish handling the outstanding event 991 // now that we've zeroed out any of our pending responses for this dead client. 992 checkResponses(responseWrangler); 993 } 994 995 // Remove our struct from gConnections 996 index = CFArrayGetFirstIndexOfValue(gConnections, connectionsRange, (void *)reap); 997 998 if (kCFNotFound != index) { 999 CFArrayRemoveValueAtIndex(gConnections, index); 1000 } 1001 1002 free(reap); 1003 1004 return; 1005} 1006 1007/*****************************************************************************/ 1008/*****************************************************************************/ 1009 1010static void cleanupResponseWrangler(PMResponseWrangler *reap) 1011{ 1012 PMConnection *one_connection; 1013 CFIndex i, connectionsCount; 1014 CFIndex responseCount; 1015 1016 long nextAcknowledgementID; 1017 int nextInterestBits; 1018 bool nextIsValid; 1019 1020 if (!reap) 1021 return; 1022 1023 if (!gConnections) 1024 return; 1025 1026 // Cache the next response fields. 1027 nextInterestBits = reap->nextInterestBits; 1028 nextAcknowledgementID = reap->nextKernelAcknowledgementID; 1029 nextIsValid = reap->nextIsValid; 1030 1031 // Loop through all connections. If any connections are referring to 1032 // responseWrangler in their tracking structs, zero that out. 1033 connectionsCount = CFArrayGetCount(gConnections); 1034 for (i=0; i<connectionsCount; i++) 1035 { 1036 one_connection = (PMConnection *)CFArrayGetValueAtIndex(gConnections, i); 1037 if (reap == one_connection->responseHandler) 1038 { 1039 // Zero out this reference before it points to a free'd pointer 1040 one_connection->responseHandler = NULL; 1041 } 1042 } 1043 1044 1045 // Loop responses, destroy responses 1046 if (reap->awaitingResponses) 1047 { 1048 responseCount = CFArrayGetCount(reap->awaitingResponses); 1049 if (responseCount > 0) 1050 { 1051 for (i=0; i<responseCount; i++) 1052 { 1053 PMResponse *purgeMe = (PMResponse *)CFArrayGetValueAtIndex(reap->awaitingResponses, i); 1054 1055 if (purgeMe->clientInfoString) 1056 CFRelease(purgeMe->clientInfoString); 1057 1058 free(purgeMe); 1059 } 1060 } 1061 CFRelease(reap->awaitingResponses); 1062 1063 reap->awaitingResponses = NULL; 1064 } 1065 1066 // Invalidate the pointer to the in-flight response wrangler. 1067 if (gLastResponseWrangler == reap) { 1068 gLastResponseWrangler = NULL; 1069 } 1070 1071 free(reap); 1072 1073 // Create a new response wrangler if the reaped wrangler has a queued 1074 // notification stored. If new wrangler is NULL, make sure to ack any 1075 // sleep message. 1076 if (nextIsValid) 1077 { 1078 PMResponseWrangler * resp; 1079 resp = connectionFireNotification(nextInterestBits, nextAcknowledgementID); 1080 if (!resp) 1081 { 1082 if (nextAcknowledgementID) 1083 IOAllowPowerChange(gRootDomainConnect, nextAcknowledgementID); 1084 } 1085 } 1086} 1087 1088/*****************************************************************************/ 1089/*****************************************************************************/ 1090 1091__private_extern__ bool PMConnectionHandleDeadName(mach_port_t deadPort) 1092{ 1093 PMConnection *one_connection = NULL;; 1094 PMConnection *the_connection = NULL; 1095 CFIndex i, connectionsCount = 0; 1096 1097 if (!gConnections) 1098 return false; 1099 1100 connectionsCount = CFArrayGetCount(gConnections); 1101 1102 // Find the PMConnection that owns this mach port 1103 for (i=0; i<connectionsCount; i++) 1104 { 1105 one_connection = (PMConnection *)CFArrayGetValueAtIndex(gConnections, i); 1106 1107 if (one_connection && (deadPort == one_connection->notifyPort)) 1108 { 1109 the_connection = one_connection; 1110 break; 1111 } 1112 } 1113 1114 if (the_connection) { 1115 cleanupConnection(the_connection); 1116 return true; 1117 } else { 1118 return false; 1119 } 1120} 1121 1122/*****************************************************************************/ 1123/*****************************************************************************/ 1124 1125static void setSystemSleepStateTracking(IOPMCapabilityBits capables) 1126{ 1127 SCDynamicStoreRef store = _getSharedPMDynamicStore(); 1128 CFStringRef key = NULL; 1129 CFNumberRef capablesNum = NULL; 1130 1131 if (!store) 1132 return; 1133 1134 key = SCDynamicStoreKeyCreate(0, CFSTR("%@%@"), 1135 kSCDynamicStoreDomainState, 1136 CFSTR(kIOPMSystemPowerCapabilitiesKeySuffix)); 1137 if (!key) 1138 return; 1139 1140 capablesNum = CFNumberCreate(0, kCFNumberIntType, &capables); 1141 1142 if (capablesNum) { 1143 PMStoreSetValue(key, capablesNum); 1144 CFRelease(capablesNum); 1145 } 1146 1147 CFRelease(key); 1148} 1149 1150#define IS_CAP_GAIN(c, f) \ 1151 ((((c)->fromCapabilities & (f)) == 0) && \ 1152 (((c)->toCapabilities & (f)) != 0)) 1153 1154 1155#pragma mark - 1156#pragma mark SleepServices 1157 1158kern_return_t _io_pm_get_uuid( 1159 mach_port_t server __unused, 1160 int selector, 1161 string_t out_uuid, 1162 int *return_code) 1163{ 1164 if (kIOPMSleepServicesUUID == selector) 1165 { 1166 if (gSleepService.uuid) 1167 { 1168 if (CFStringGetCString(gSleepService.uuid, out_uuid, kPMMIGStringLength, kCFStringEncodingUTF8)) 1169 { 1170 *return_code = kIOReturnSuccess; 1171 return KERN_SUCCESS; 1172 } 1173 } 1174 } 1175 out_uuid[0] = '\0'; 1176 *return_code = kIOReturnNotFound; 1177 return KERN_SUCCESS; 1178} 1179 1180kern_return_t _io_pm_set_sleepservice_wake_time_cap( 1181 mach_port_t server __unused, 1182 audit_token_t token, 1183 int cap_ms, 1184 int *return_code) 1185{ 1186#if !TARGET_OS_EMBEDDED 1187 pid_t callerPID = -1; 1188 uid_t callerUID = -1; 1189 gid_t callerGID = -1; 1190 1191 audit_token_to_au32(token, NULL, NULL, NULL, &callerUID, &callerGID, &callerPID, NULL, NULL); 1192 if ( !callerIsRoot(callerUID) ) 1193 { 1194 *return_code = kIOReturnNotPrivileged; 1195 return KERN_SUCCESS; 1196 } 1197 1198 if (gSleepService.capTime == 0 || 1199 !isA_SleepSrvcWake() ) 1200 { 1201 *return_code = kIOReturnError; 1202 return KERN_SUCCESS; 1203 } 1204 gSleepService.capTime = cap_ms; 1205 setSleepServicesTimeCap(cap_ms); 1206#endif 1207 *return_code = kIOReturnSuccess; 1208 return KERN_SUCCESS; 1209} 1210 1211#if !TARGET_OS_EMBEDDED 1212static void scheduleSleepServiceCapTimerEnforcer(uint32_t cap_ms) 1213{ 1214 if(!cap_ms) 1215 return; 1216 1217 CFMutableDictionaryRef assertionDescription = NULL; 1218 1219 // Don't allow SS sessions when 'PreventSystemSleep' assertions are held 1220 if (checkForActivesByType(kPreventSleepType)) 1221 return; 1222 1223 gSleepService.capTime = (long)cap_ms; 1224 1225 setSleepServicesTimeCap(cap_ms); 1226 /* 1227 * Publish a SleepService UUID 1228 */ 1229 1230 if (gSleepService.uuid) { 1231 CFRelease(gSleepService.uuid); 1232 } 1233 CFUUIDRef ssuuid = NULL; 1234 ssuuid = CFUUIDCreate(0); 1235 if (ssuuid) { 1236 gSleepService.uuid = CFUUIDCreateString(0, ssuuid); 1237 CFRelease(ssuuid); 1238 } 1239 1240 gPowerState |= kDarkWakeForSSState; 1241 configAssertionType(kPushServiceTaskType, false); 1242 configAssertionType(kInteractivePushServiceType, false); 1243 assertionDescription = _IOPMAssertionDescriptionCreate( 1244 kIOPMAssertionTypeApplePushServiceTask, 1245 CFSTR("Powerd - Wait for client pushService assertions"), 1246 NULL, NULL, NULL, 1247 10, kIOPMAssertionTimeoutActionRelease); 1248 1249 InternalCreateAssertion(assertionDescription, NULL); 1250 1251 CFRelease(assertionDescription); 1252 1253 /* 1254 * Publish SleepService notify state under "com.apple.powermanagement.sleepservices" 1255 */ 1256 1257 if (!gSleepService.notifyToken) { 1258 int status; 1259 status = notify_register_check(kIOPMSleepServiceActiveNotifyName, &gSleepService.notifyToken); 1260 1261 if (NOTIFY_STATUS_OK != status) { 1262 gSleepService.notifyToken = 0; 1263 } 1264 } 1265 1266 if (gSleepService.notifyToken) 1267 { 1268 /* Interested clients will know that PM is in a SleepServices wake, as dictated by SleepServiceD */ 1269 notify_set_state(gSleepService.notifyToken, kIOPMSleepServiceActiveNotifyBit); 1270 notify_post(kIOPMSleepServiceActiveNotifyName); 1271 } 1272 1273 /* 1274 * Announce to ASL & MessageTracer 1275 */ 1276 logASLMessageSleepServiceBegins(gSleepService.capTime); 1277 1278 1279 if (!gNotifySleepServiceToken) { 1280 int status; 1281 status = notify_register_check(kIOPMSleepServiceActiveNotifyName, &gNotifySleepServiceToken); 1282 1283 if (NOTIFY_STATUS_OK != status) { 1284 gNotifySleepServiceToken = 0; 1285 } 1286 } 1287 1288 if (gNotifySleepServiceToken) 1289 { 1290 /* Interested clients will know that PM is in a SleepServices wake, as dictated by SleepServiceD */ 1291 notify_set_state(gNotifySleepServiceToken, kIOPMSleepServiceActiveNotifyBit); 1292 notify_post(kIOPMSleepServiceActiveNotifyName); 1293 } 1294} 1295#endif 1296 1297 1298#if LOG_SLEEPSERVICES 1299 1300/*****************************************************************************/ 1301/* LOG ASL MESSAGE SLEEPSERVICE - Begins */ 1302/*****************************************************************************/ 1303 1304#if !TARGET_OS_EMBEDDED 1305 1306static void logASLMessageSleepServiceBegins(long withCapTime) 1307{ 1308 aslmsg m; 1309 char strbuf[125]; 1310 1311 m = new_msg_pmset_log(); 1312 1313 asl_set(m, kPMASLDomainKey, kPMASLDomainSleepServiceStarted); 1314 1315 /* com.apple.message.uuid = <SleepWake UUID> 1316 */ 1317 if (_getUUIDString(strbuf, sizeof(strbuf))) { 1318 asl_set(m, kPMASLUUIDKey, strbuf); 1319 } 1320 1321 /* com.apple.message.uuid2 = <SleepServices UUID> 1322 */ 1323 if (gSleepService.uuid 1324 && CFStringGetCString(gSleepService.uuid, strbuf, sizeof(strbuf), kCFStringEncodingUTF8)) 1325 { 1326 asl_set(m, kPMASLUUID2Key, strbuf); 1327 } 1328 1329 snprintf(strbuf, sizeof(strbuf), "%ld", withCapTime); 1330 asl_set(m, kPMASLValueKey, strbuf); 1331 1332 snprintf(strbuf, sizeof(strbuf), "SleepService: window begins with cap time=%ld secs", withCapTime/1000); 1333 asl_set(m, ASL_KEY_MSG, strbuf); 1334 1335 asl_send(NULL, m); 1336 asl_release(m); 1337} 1338#endif 1339 1340/*****************************************************************************/ 1341/* LOG ASL MESSAGE SLEEPSERVICE - Terminated*/ 1342/*****************************************************************************/ 1343 1344__private_extern__ void logASLMessageSleepServiceTerminated(int forcedTimeoutCnt) 1345{ 1346 aslmsg m; 1347 char strUUID[100]; 1348 char strUUID2[100]; 1349 char valStr[30]; 1350 1351 if ( (gPowerState & kDarkWakeForSSState) == 0) 1352 return; 1353 1354 gPowerState &= ~kDarkWakeForSSState; 1355 configAssertionType(kPushServiceTaskType, false); 1356 configAssertionType(kInteractivePushServiceType, false); 1357 1358 m = new_msg_pmset_log(); 1359 1360 asl_set(m, kPMASLDomainKey, kPMASLDomainSleepServiceTerminated); 1361 1362 /* com.apple.message.uuid = <SleepWake UUID> 1363 */ 1364 if (_getUUIDString(strUUID, sizeof(strUUID))) { 1365 asl_set(m, kPMASLUUIDKey, strUUID); 1366 } 1367 1368 /* com.apple.message.uuid2 = <SleepServices UUID> 1369 */ 1370 if (gSleepService.uuid 1371 && CFStringGetCString(gSleepService.uuid, strUUID2, sizeof(strUUID2), kCFStringEncodingUTF8)) 1372 { 1373 asl_set(m, kPMASLUUID2Key, strUUID2); 1374 } 1375 1376 /* value = # of clients whose assertions had to be timed out. 1377 */ 1378 snprintf(valStr, sizeof(valStr), "%d", forcedTimeoutCnt); 1379 asl_set(m, kPMASLValueKey, valStr); 1380 1381 1382 asl_set(m, ASL_KEY_MSG, "SleepService: window has terminated."); 1383 1384 /* Signature for how many clients timed out 1385 */ 1386 if (forcedTimeoutCnt == 0) 1387 { 1388 asl_set(m, kPMASLSignatureKey, kPMASLSigSleepServiceExitClean); 1389 } else { 1390 asl_set(m, kPMASLSignatureKey, kPMASLSigSleepServiceTimedOut); 1391 } 1392 1393 asl_send(NULL, m); 1394 asl_release(m); 1395 1396 /* Messages describes the next state - S3, S0Dark, S0 1397 */ 1398/* 1399 IOACPISystemPowerLevel minSystemPower; 1400 1401 minSystemPower = getAssertionsMinimumSystemPowerLevel(); 1402 1403 if (kS0Dark == minSystemPower) { 1404 asl_set(endMsg, ASL_KEY_MSG, "SleepService window terminated. Elevated to DarkWake."); 1405 } else 1406 if (kS0Full == minSystemPower) { 1407 asl_set(endMsg, ASL_KEY_MSG, "SleepService window terminated. Elevated to FullWake."); 1408 } else 1409 if (kS3 == minSystemPower) { 1410 asl_set(endMsg, ASL_KEY_MSG, "SleepService window terminated. Returning to S3/S4."); 1411 } else { 1412 1413 asl_set(endMsg, ASL_KEY_MSG, "SleepService window terminated. AssertionsMinimumSystemPowerLevel = Unknown."); 1414 } 1415*/ 1416} 1417#endif 1418 1419/*****************************************************************************/ 1420/* Getters for System State */ 1421/*****************************************************************************/ 1422bool isA_SleepState() 1423{ 1424 if (gPowerState & kSleepState) 1425 return true; 1426 return false; 1427} 1428__private_extern__ bool isA_DarkWakeState() 1429{ 1430 if (gPowerState & kDarkWakeState) 1431 return true; 1432 1433 return false; 1434 1435} 1436__private_extern__ bool isA_BTMtnceWake() 1437{ 1438 1439 if (gPowerState & kDarkWakeForBTState) 1440 return true; 1441 1442 return false; 1443} 1444 1445__private_extern__ void set_SleepSrvcWake() 1446{ 1447 gPowerState |= kDarkWakeForSSState; 1448} 1449 1450__private_extern__ void set_NotificationDisplayWake() 1451{ 1452 gPowerState |= kNotificationDisplayWakeState; 1453} 1454 1455__private_extern__ void cancel_NotificationDisplayWake() 1456{ 1457 gPowerState &= ~kNotificationDisplayWakeState; 1458} 1459 1460__private_extern__ bool isA_NotificationDisplayWake() 1461{ 1462 1463 if (gPowerState & kNotificationDisplayWakeState) 1464 return true; 1465 1466 return false; 1467} 1468 1469__private_extern__ bool isA_SleepSrvcWake() 1470{ 1471 1472 if (gPowerState & kDarkWakeForSSState) 1473 return true; 1474 1475 return false; 1476} 1477 1478__private_extern__ void cancelPowerNapStates( ) 1479{ 1480 1481#if !TARGET_OS_EMBEDDED 1482 if (isA_SleepSrvcWake()) { 1483 gSleepService.capTime = 0; 1484 setSleepServicesTimeCap(0); 1485 } 1486 1487 if (isA_BTMtnceWake() ) { 1488 gPowerState &= ~kDarkWakeForBTState; 1489 configAssertionType(kBackgroundTaskType, false); 1490 } 1491 1492 SystemLoadSystemPowerStateHasChanged( ); 1493 1494#endif 1495} 1496 1497/*****************************************************************************/ 1498/* Auto Power Off Timers */ 1499/*****************************************************************************/ 1500 1501/* 1502 * Function to query IOPPF(thru rootDomain) the next possible sleep type 1503 */ 1504static kern_return_t 1505getPlatformSleepType( uint32_t *sleepType ) 1506{ 1507 1508 IOReturn ret; 1509 uint32_t outputScalarCnt = 1; 1510 size_t outputStructSize = 0; 1511 uint64_t outs[2]; 1512 1513 1514 ret = IOConnectCallMethod( 1515 gRootDomainConnect, // connect 1516 kPMGetSystemSleepType, // selector 1517 NULL, // input 1518 0, // input count 1519 NULL, // input struct 1520 0, // input struct count 1521 outs, // output scalar 1522 &outputScalarCnt, // output scalar count 1523 NULL, // output struct 1524 &outputStructSize); // output struct size 1525 1526 if (kIOReturnSuccess == ret) 1527 *sleepType = (uint32_t)outs[0]; 1528 1529 1530 return ret; 1531 1532} 1533 1534static void startAutoPowerOffSleep( ) 1535{ 1536 uint32_t sleepType = kIOPMSleepTypeInvalid; 1537 CFTimeInterval nextAutoWake = 0.0; 1538 1539 if (isA_SleepState()) { 1540 /* 1541 * IOPPF can't return proper sleep type when system is still not 1542 * completely up. Check again after system wake is completed. 1543 */ 1544 return; 1545 } 1546 /* 1547 * Check with IOPPF(thru root domain) if system is in a state to 1548 * go into auto power off state. Also, make sure that there are 1549 * no user scheduled wake requests 1550 */ 1551 getPlatformSleepType( &sleepType ); 1552 nextAutoWake = getEarliestRequestAutoWake(); 1553 if ( (sleepType != kIOPMSleepTypePowerOff) || (nextAutoWake != 0) ) { 1554 1555 if (gDebugFlags & kIOPMDebugLogCallbacks) 1556 asl_log(0,0,ASL_LEVEL_ERR, "Resetting APO timer. sleepType:%d nextAutoWake:%f\n", 1557 sleepType, nextAutoWake); 1558 setAutoPowerOffTimer(false, 0); 1559 return; 1560 } 1561 1562 if (gDebugFlags & kIOPMDebugLogCallbacks) 1563 asl_log(0,0,ASL_LEVEL_ERR, "Cancelling assertions for AutoPower Off\n"); 1564 cancelAutoPowerOffTimer( ); 1565 /* 1566 * We will be here only if the system is in dark wake. In that 1567 * case, 'kPreventIdleIndex' assertion type is not honoured any more(except if it 1568 * is a network wake, in which case system is not auto-Power off capable). 1569 * If there are any assertions of type 'kPreventSleepType', then system is in 1570 * server mode, which again make system not auto-power off capable. 1571 * 1572 * So, disabling 'kBackgroundTaskType' & 'kPushServiceTaskType' is good enough 1573 */ 1574 cancelPowerNapStates( ); 1575} 1576 1577void cancelAutoPowerOffTimer() 1578{ 1579 1580 if (gApoDispatch) 1581 dispatch_source_cancel(gApoDispatch); 1582} 1583 1584/* 1585 * setAutoPowerOffTimer: starts a timer to fire a little before the point when system 1586 * need to go into auto power off mode. Also sets the variable ts_apo, 1587 * indicating absolute time at which system need to go into APO mode. 1588 * Params:initialCall - set to true if this is called when system is going fom S0->S3 1589 * postponeAfter - When set to non-zero, timer is postponed by 'autopoweroffdelay' secs 1590 * after the specified 'postponeAfter' value. Other wise, timer is set to 1591 * fire after 'autopowerofdelay' secs from current time. 1592 */ 1593void setAutoPowerOffTimer(bool initialCall, CFAbsoluteTime postponeAfter) 1594{ 1595 int64_t apo_enable, apo_delay; 1596 int64_t timer_fire; 1597 CFAbsoluteTime cur_time = 0.0; 1598 1599 if ( (GetPMSettingNumber(CFSTR(kIOPMAutoPowerOffEnabledKey), &apo_enable) != kIOReturnSuccess) || 1600 (apo_enable != 1) ) { 1601 if (gDebugFlags & kIOPMDebugLogCallbacks) 1602 asl_log(0,0,ASL_LEVEL_ERR, "Failed to get APO enabled key\n"); 1603 ts_apo = 0; 1604 return; 1605 } 1606 1607 if ( (GetPMSettingNumber(CFSTR(kIOPMAutoPowerOffDelayKey), &apo_delay) != kIOReturnSuccess) ) { 1608 if (gDebugFlags & kIOPMDebugLogCallbacks) 1609 asl_log(0,0,ASL_LEVEL_ERR, "Failed to get APO delay timer \n"); 1610 ts_apo = 0; 1611 return; 1612 } 1613 1614 cur_time = CFAbsoluteTimeGetCurrent(); 1615 if ( postponeAfter > cur_time ) 1616 ts_apo = postponeAfter + apo_delay; 1617 else 1618 ts_apo = cur_time + apo_delay; 1619 1620 if (apo_delay > kAutoPowerOffSleepAhead) { 1621 /* 1622 * Let the timer fire little ahead of actual auto power off time, to allow system 1623 * go to sleep and wake up for auto power off. 1624 */ 1625 timer_fire = (ts_apo - cur_time) - kAutoPowerOffSleepAhead; 1626 } 1627 else if ( !initialCall ) { 1628 /* 1629 * For repeat checks, give at least 'apo_delay' secs 1630 * before checking the sleep state 1631 */ 1632 if ( postponeAfter > cur_time ) 1633 timer_fire = (ts_apo - cur_time) - apo_delay; 1634 else 1635 timer_fire = apo_delay; 1636 } 1637 else { 1638 /* No need of timer. Let the system go to sleep if Auto-power off is allowed */ 1639 startAutoPowerOffSleep(); 1640 1641 return; 1642 } 1643 1644 1645 if (gApoDispatch) 1646 dispatch_suspend(gApoDispatch); 1647 else { 1648 gApoDispatch = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 1649 0, dispatch_get_main_queue()); 1650 dispatch_source_set_event_handler(gApoDispatch, ^{ 1651 startAutoPowerOffSleep(); 1652 }); 1653 1654 dispatch_source_set_cancel_handler(gApoDispatch, ^{ 1655 if (gApoDispatch) { 1656 dispatch_release(gApoDispatch); 1657 gApoDispatch = 0; 1658 } 1659 }); 1660 1661 } 1662 1663 if (gDebugFlags & kIOPMDebugLogCallbacks) 1664 asl_log(0,0,ASL_LEVEL_ERR, 1665 "Set auto power off timer to fire in %lld secs\n", timer_fire); 1666 dispatch_source_set_timer(gApoDispatch, 1667 dispatch_walltime(NULL, timer_fire * NSEC_PER_SEC), DISPATCH_TIME_FOREVER, 0); 1668 dispatch_resume(gApoDispatch); 1669 1670} 1671 1672/* Evaulate for Power source change */ 1673void evalForPSChange( ) 1674{ 1675 int pwrSrc; 1676 static int prevPwrSrc = -1; 1677 1678 pwrSrc = _getPowerSource(); 1679 if (pwrSrc == prevPwrSrc) 1680 return; // If power source hasn't changed, there is nothing to do 1681 1682 prevPwrSrc = pwrSrc; 1683 1684#if !TARGET_OS_EMBEDDED 1685 if (gSleepService.capTime != 0 && 1686 isA_SleepSrvcWake()) { 1687 int capTimeout = getCurrentSleepServiceCapTimeout(); 1688 gSleepService.capTime = capTimeout; 1689 setSleepServicesTimeCap(capTimeout); 1690 } 1691#endif 1692 1693 if (pwrSrc == kBatteryPowered) 1694 return; 1695 1696 if ((pwrSrc == kACPowered) && gApoDispatch ) { 1697 setAutoPowerOffTimer(false, 0); 1698 } 1699} 1700 1701__private_extern__ void InternalEvalConnections(void) 1702{ 1703 CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, ^{ 1704 evalForPSChange(); 1705 }); 1706 CFRunLoopWakeUp(_getPMRunLoop()); 1707 1708} 1709 1710static void handleLastCallMsg(void *messageData) 1711{ 1712 1713 if( 1714#if TCPKEEPALIVE 1715 ((getTCPKeepAliveState(NULL, 0) == kActive) && checkForActivesByType(kInteractivePushServiceType)) || 1716#endif 1717 checkForActivesByType(kDeclareSystemActivityType) ) 1718 { 1719 logASLMessageSleepCanceledAtLastCall(); 1720#if !TARGET_OS_EMBEDDED 1721 /* Log all assertions that could have canceled sleep */ 1722 CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, 1723 ^{ logASLAssertionTypeSummary(kInteractivePushServiceType); 1724 logASLAssertionTypeSummary(kDeclareSystemActivityType);}); 1725 CFRunLoopWakeUp(_getPMRunLoop()); 1726#endif 1727 1728 IOCancelPowerChange(gRootDomainConnect, (long)messageData); 1729 } 1730 else 1731 { 1732 IOAllowPowerChange(gRootDomainConnect, (long)messageData); 1733 gMachineStateRevertible = false; 1734 } 1735 1736} 1737 1738static void handleCanSleepMsg(void *messageData, CFStringRef sleepReason) 1739{ 1740 bool allow_sleep = true; 1741 1742#if !TARGET_OS_EMBEDDED 1743 /* Check for active TTYs due to ssh sessiosn */ 1744 allow_sleep = TTYKeepAwakeConsiderAssertion( ); 1745#endif 1746 1747 if (allow_sleep) { 1748 IOAllowPowerChange(gRootDomainConnect, (long)messageData); 1749 if (CFStringCompare(sleepReason, CFSTR(kIOPMIdleSleepKey), 0) != kCFCompareEqualTo) { 1750 gMachineStateRevertible = false; 1751 } 1752 } 1753 else 1754 IOCancelPowerChange(gRootDomainConnect, (long)messageData); 1755 1756 1757} 1758 1759/*****************************************************************************/ 1760/* PMConnectionPowerCallBack */ 1761/*****************************************************************************/ 1762 1763#pragma mark - 1764#pragma mark Sleep/Wake 1765 1766static void PMConnectionPowerCallBack( 1767 void *port, 1768 io_service_t rootdomainservice, 1769 natural_t inMessageType, 1770 void *messageData) 1771{ 1772 PMResponseWrangler *responseController = NULL; 1773 IOPMCapabilityBits deliverCapabilityBits = 0; 1774 const struct IOPMSystemCapabilityChangeParameters *capArgs; 1775 CFAbsoluteTime cur_time = 0.0; 1776 CFStringRef sleepReason = _getSleepReason(); 1777 static bool slp_logd = false; 1778 static bool s02dw_logd = false; 1779 static bool slp2dw_logd = false; 1780 CFArrayRef appStats = NULL; 1781#if !TARGET_OS_EMBEDDED 1782 CFStringRef wakeType = CFSTR(""); 1783#endif 1784 1785 if(inMessageType == kIOPMMessageLastCallBeforeSleep) 1786 { 1787 handleLastCallMsg(messageData); 1788 return; 1789 } 1790 else if (inMessageType == kIOMessageCanSystemSleep) 1791 { 1792 sleepReason = _updateSleepReason(); 1793 handleCanSleepMsg(messageData, sleepReason); 1794 return; 1795 } 1796 else if (inMessageType == kIOMessageSystemWillNotSleep) 1797 { 1798 gMachineStateRevertible = true; 1799 return; 1800 } 1801 else if (kIOMessageSystemCapabilityChange != inMessageType) 1802 return; 1803 1804 capArgs = (const struct IOPMSystemCapabilityChangeParameters *)messageData; 1805 1806 AutoWakeCapabilitiesNotification(capArgs->fromCapabilities, capArgs->toCapabilities); 1807 ClockSleepWakeNotification(capArgs->fromCapabilities, capArgs->toCapabilities, 1808 capArgs->changeFlags); 1809 1810 if (IS_DARK_CAPABILITIES(capArgs->fromCapabilities) 1811 && !IS_DARK_CAPABILITIES(capArgs->toCapabilities)) 1812 { 1813 /* MessageTracer only records assertion events that occurred during DarkWake. 1814 * This cues MT2 to stop logging assertions. 1815 */ 1816 mt2DarkWakeEnded(); 1817 } 1818 1819 if (SYSTEM_WILL_SLEEP_TO_S0(capArgs)) 1820 { 1821 // Send an async notify out - clients include SCNetworkReachability API's; no ack expected 1822 setSystemSleepStateTracking(0); 1823 notify_post(kIOPMSystemPowerStateNotify); 1824 1825#if !TARGET_OS_EMBEDDED 1826 _resetWakeReason(); 1827 // If this is a non-SR wake, set next earliest PowerNap wake point to 1828 // after 'kPMSleepDurationForBT' secs. 1829 // For all other wakes, don't move the next PowerNap wake point. 1830 if ( ( gCurrentSilentRunningState == kSilentRunningOff ) || 1831 CFEqual(sleepReason, CFSTR(kIOPMDarkWakeThermalEmergencyKey))){ 1832 ts_nextPowerNap = CFAbsoluteTimeGetCurrent() + kPMSleepDurationForBT; 1833 1834 } 1835 cancelPowerNapStates(); 1836#endif 1837 1838 // Clear gPowerState and set it to kSleepState 1839 // We will clear kDarkWakeForSSState bit later when the SS session is closed. 1840 gPowerState &= ~(kPowerStateMask ^ kDarkWakeForSSState); 1841 gPowerState |= kSleepState; 1842 1843 1844 /* We must acknowledge this sleep event within 30 second timeout, 1845 * once clients ack via IOPMConnectionAcknowledgeEvent(). 1846 * Our processing will pick-up again in our handler 1847 * _io_pm_connection_acknowledge_event 1848 */ 1849 logASLMessageSleep(kPMASLSigSuccess, NULL, NULL, kIsS0Sleep); 1850 slp_logd = true; 1851 1852 // Tell flight data recorder we're going to sleep 1853 recordFDREvent(kFDRBattEventAsync, false, _batteries()); 1854 recordFDREvent(kFDRSleepEvent, false, NULL); 1855 1856 if(smcSilentRunningSupport() ) 1857 gCurrentSilentRunningState = kSilentRunningOn; 1858 1859 responseController = connectionFireNotification(_kSleepStateBits, (long)capArgs->notifyRef); 1860 1861 1862 1863 1864 if (!responseController) { 1865 // We have zero clients. Acknowledge immediately. 1866 1867 PMScheduleWakeEventChooseBest(getEarliestRequestAutoWake(), kChooseFullWake); 1868 IOAllowPowerChange(gRootDomainConnect, (long)capArgs->notifyRef); 1869 } 1870 1871 return; 1872#if !TARGET_OS_EMBEDDED 1873 } else if (SYSTEM_WILL_SLEEP_TO_S0DARK(capArgs)) 1874 { 1875 bool linger = false; 1876 bool blockedInS0 = systemBlockedInS0Dark(); 1877 setAutoPowerOffTimer(true, 0); 1878 1879 1880 gPowerState |= kDarkWakeState; 1881 gPowerState &= ~kNotificationDisplayWakeState; 1882 1883 1884 // Dark Wake Linger: After going from FullWake to DarkWake during a 1885 // demand sleep, linger in darkwake for a certain amount of seconds 1886 // 1887 // Power Nap machines will linger on every FullWake --> DarkWake 1888 // transition except emergency sleeps. 1889 // Non-Power Nap machines will linger on every FullWake --> DarkWake 1890 // transition except emergency sleeps, and clamshell close sleeps 1891 bool isBTCapable = (IOPMFeatureIsAvailable(CFSTR(kIOPMDarkWakeBackgroundTaskKey), NULL) || 1892 gForceDWL)?true:false; 1893 1894 bool isEmergencySleep = (CFEqual(sleepReason, CFSTR(kIOPMLowPowerSleepKey)) || 1895 CFEqual(sleepReason, CFSTR(kIOPMThermalEmergencySleepKey)))?true:false; 1896 bool isClamshellSleep = CFEqual(sleepReason, CFSTR(kIOPMClamshellSleepKey))?true:false; 1897 1898 if(((isBTCapable && !isEmergencySleep) || 1899 (!isBTCapable && !isEmergencySleep && !isClamshellSleep)) && 1900 (kPMDarkWakeLingerDuration != 0)) 1901 { 1902 1903 CFMutableDictionaryRef assertionDescription = NULL; 1904 assertionDescription = _IOPMAssertionDescriptionCreate( 1905 kIOPMAssertInternalPreventSleep, 1906 CFSTR("com.apple.powermanagement.darkwakelinger"), 1907 NULL, CFSTR("Proxy assertion to linger in darkwake"), 1908 NULL, kPMDarkWakeLingerDuration, 1909 kIOPMAssertionTimeoutActionRelease); 1910 1911 if (assertionDescription) 1912 { 1913 //This assertion should be applied even on battery power 1914 CFDictionarySetValue(assertionDescription, 1915 kIOPMAssertionAppliesToLimitedPowerKey, 1916 (CFBooleanRef)kCFBooleanTrue); 1917 InternalCreateAssertion(assertionDescription, NULL); 1918 1919 CFRelease(assertionDescription); 1920 1921 linger = true; 1922 } 1923 } 1924 1925 /* 1926 * This notification is issued before every sleep. Log to asl only if 1927 * there are assertion that can prevent system from going into S3/S4 1928 */ 1929 if (linger || blockedInS0) { 1930 1931 // Take off Audio & Video capabilities. Leave other capabilities as is. 1932 deliverCapabilityBits = (gCurrentCapabilityBits & ~(kIOPMCapabilityAudio|kIOPMCapabilityVideo)); 1933 1934 if (blockedInS0) { 1935 logASLMessageSleep(kPMASLSigSuccess, NULL, NULL, kIsDarkWake); 1936 recordFDREvent(kFDRDarkWakeEvent, false, NULL); 1937 s02dw_logd = true; 1938 1939 if (_DWBT_allowed( )) 1940 deliverCapabilityBits |= kIOPMCapabilityBackgroundTask; 1941 } 1942 1943 1944 // Send a notification with no expectation for response 1945 gCurrentCapabilityBits = deliverCapabilityBits; 1946 sendNoRespNotification(deliverCapabilityBits); 1947 } 1948#if TCPKEEPALIVE 1949 startTCPKeepAliveExpTimer(); 1950#endif 1951 1952#endif 1953 } else if (SYSTEM_DID_WAKE(capArgs)) 1954 { 1955#if !TARGET_OS_EMBEDDED 1956 _updateWakeReason(NULL, &wakeType); 1957 // On a SilentRunningMachine, the assumption is that every wake is 1958 // Silent until powerd unclamps SilentRunning or unforeseen thermal 1959 // constraints arise 1960#endif 1961 if(smcSilentRunningSupport() ) 1962 gCurrentSilentRunningState = kSilentRunningOn; 1963 1964 gMachineStateRevertible = true; 1965 1966 if (IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityGraphics)) 1967 { 1968 // e.g. System has powered into full wake 1969#if !TARGET_OS_EMBEDDED 1970 // Unclamp SilentRunning if this full wake is not due to Notification display 1971 if (CFEqual(wakeType, kIOPMRootDomainWakeTypeNotification)) { 1972 // For notification display wake, keep the 'BPS' capability bits if they 1973 // are currently set 1974 deliverCapabilityBits = (gCurrentCapabilityBits & ( 1975 (kIOPMCapabilityPushServiceTask|kIOPMCapabilityBackgroundTask|kIOPMCapabilitySilentRunning))); 1976 1977 gPowerState |= kFullWakeState; 1978 } 1979 else { 1980 cancelPowerNapStates(); 1981 _unclamp_silent_running(false); 1982 1983 // kDarkWakeForSSState bit is removed when the SS session is closed. 1984 gPowerState &= ~(kPowerStateMask ^ kDarkWakeForSSState); 1985 gPowerState |= kFullWakeState; 1986 } 1987 cancelAutoPowerOffTimer(); 1988#else 1989 gPowerState &= ~(kPowerStateMask ^ kDarkWakeForSSState); 1990 gPowerState |= kFullWakeState; 1991#endif 1992 deliverCapabilityBits |= 1993 kIOPMCapabilityCPU | kIOPMCapabilityDisk 1994 | kIOPMCapabilityNetwork | kIOPMCapabilityAudio | kIOPMCapabilityVideo; 1995 1996 if (BIT_IS_SET(capArgs->fromCapabilities, kIOPMSystemCapabilityCPU)) { 1997 if (slp_logd) { 1998 /* This can happen if sleep is cancelled after powerd logs system entering S3 */ 1999 logASLMessageWake(kPMASLSigSuccess, NULL, NULL, deliverCapabilityBits, kIsFullWake); 2000 } 2001 2002 else if (s02dw_logd || slp2dw_logd) { 2003 logASLMessageWake(kPMASLSigSuccess, NULL, NULL, deliverCapabilityBits, kIsDarkToFullWake); 2004 } 2005 recordFDREvent(kFDRUserWakeEvent, false, NULL); 2006 } else { 2007 if (slp_logd) { 2008 logASLMessageWake(kPMASLSigSuccess, NULL, NULL, deliverCapabilityBits, kIsFullWake); 2009 } 2010 mt2RecordWakeEvent(kWakeStateFull); 2011 recordFDREvent(kFDRUserWakeEvent, true, NULL); 2012 } 2013 slp_logd = s02dw_logd = slp2dw_logd = false; 2014 2015 } else if (IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityCPU)) 2016 { 2017 //If system is in any power nap state, cancel those states 2018 cancelPowerNapStates(); 2019 2020 // e.g. System has moved into dark wake 2021 deliverCapabilityBits = 2022 kIOPMCapabilityCPU | kIOPMCapabilityDisk | kIOPMCapabilityNetwork; 2023 2024 2025 // kDarkWakeForSSState bit is removed when the SS session is closed. 2026 gPowerState &= ~(kPowerStateMask ^ kDarkWakeForSSState); 2027 gPowerState |= kDarkWakeState; 2028 2029 _ProxyAssertions(capArgs); 2030 cur_time = CFAbsoluteTimeGetCurrent(); 2031 2032#if !TARGET_OS_EMBEDDED 2033 if ( (cur_time >= ts_nextPowerNap) && 2034 (CFEqual(wakeType, kIOPMRootDomainWakeTypeMaintenance) || 2035 CFEqual(wakeType, kIOPMRootDomainWakeTypeSleepService)) ) { 2036 2037 if ( _DWBT_allowed() ) { 2038 gPowerState |= kDarkWakeForBTState; 2039 configAssertionType(kBackgroundTaskType, false); 2040 /* Log all background task assertions once again */ 2041 CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, 2042 ^{ logASLAssertionTypeSummary(kBackgroundTaskType); }); 2043 CFRunLoopWakeUp(_getPMRunLoop()); 2044 2045 deliverCapabilityBits |= (kIOPMCapabilityBackgroundTask | 2046 kIOPMCapabilityPushServiceTask | 2047 kIOPMCapabilitySilentRunning); 2048 scheduleSleepServiceCapTimerEnforcer( 2049 getCurrentSleepServiceCapTimeout()); 2050 } 2051 else if (_SS_allowed() ) { 2052 deliverCapabilityBits |= (kIOPMCapabilityPushServiceTask | 2053 kIOPMCapabilitySilentRunning); 2054 scheduleSleepServiceCapTimerEnforcer( 2055 getCurrentSleepServiceCapTimeout()); 2056 } 2057 } 2058 else if (CFEqual(wakeType, kIOPMRootDomainWakeTypeNetwork)) { 2059 if (_DWBT_allowed()) { 2060 deliverCapabilityBits |= (kIOPMCapabilityBackgroundTask | 2061 kIOPMCapabilityPushServiceTask | 2062 kIOPMCapabilitySilentRunning); 2063 scheduleSleepServiceCapTimerEnforcer( 2064 getCurrentSleepServiceCapTimeout()); 2065 2066 } 2067 else if (_SS_allowed() ) { 2068 deliverCapabilityBits |= (kIOPMCapabilityPushServiceTask | 2069 kIOPMCapabilitySilentRunning); 2070 2071 scheduleSleepServiceCapTimerEnforcer( 2072 getCurrentSleepServiceCapTimeout()); 2073 } 2074 } 2075 else { 2076 if ( !CFEqual(wakeType, kIOPMrootDomainWakeTypeLowBattery) && 2077 !CFEqual(wakeType, kIOPMRootDomainWakeTypeSleepTimer) ) { 2078 2079 if (_SS_allowed() ) { 2080 deliverCapabilityBits |= (kIOPMCapabilityPushServiceTask | 2081 kIOPMCapabilitySilentRunning); 2082 2083 scheduleSleepServiceCapTimerEnforcer( 2084 getCurrentSleepServiceCapTimeout()); 2085 } 2086 } 2087 } 2088 if (checkForActivesByType(kPreventSleepType)) { 2089 deliverCapabilityBits &= ~kIOPMCapabilitySilentRunning; 2090 _unclamp_silent_running(false); 2091 } 2092 2093 if ((kACPowered == _getPowerSource()) && (kPMDarkWakeLingerDuration != 0)) { 2094 CFMutableDictionaryRef assertionDescription = NULL; 2095 assertionDescription = _IOPMAssertionDescriptionCreate( 2096 kIOPMAssertInternalPreventSleep, 2097 CFSTR("com.apple.powermanagement.acwakelinger"), 2098 NULL, CFSTR("Proxy assertion to linger on darkwake with ac"), 2099 NULL, kPMACWakeLingerDuration, 2100 kIOPMAssertionTimeoutActionRelease); 2101 2102 InternalCreateAssertion(assertionDescription, NULL); 2103 CFRelease(assertionDescription); 2104 } 2105#endif 2106 if (gApoDispatch && ts_apo && (cur_time > ts_apo - kAutoPowerOffSleepAhead) ) { 2107 /* Check for auto-power off if required */ 2108 dispatch_suspend(gApoDispatch); 2109 dispatch_source_set_timer(gApoDispatch, 2110 DISPATCH_TIME_NOW, DISPATCH_TIME_FOREVER, 0); 2111 dispatch_resume(gApoDispatch); 2112 } 2113 if (slp_logd) { 2114 logASLMessageWake(kPMASLSigSuccess, NULL, NULL, deliverCapabilityBits, kIsDarkWake); 2115 slp2dw_logd = true; 2116 slp_logd = false; 2117 } 2118 mt2RecordWakeEvent(kWakeStateDark); 2119 recordFDREvent(kFDRDarkWakeEvent, true, NULL); 2120 } 2121 2122 // Send an async notify out - clients include SCNetworkReachability API's; no ack expected 2123 setSystemSleepStateTracking(deliverCapabilityBits); 2124 2125 notify_post(kIOPMSystemPowerStateNotify); 2126 2127 responseController = connectionFireNotification(deliverCapabilityBits, (long)capArgs->notifyRef); 2128 2129 /* We must acknowledge this sleep event within 30 second timeout, 2130 * once clients ack via IOPMConnectionAcknowledgeEvent(). 2131 * Our processing will pick-up again in our handler 2132 * _io_pm_connection_acknowledge_event 2133 */ 2134 if (!responseController) { 2135 // We have zero clients. Acknowledge immediately. 2136 IOAllowPowerChange(gRootDomainConnect, (long)capArgs->notifyRef); 2137 } 2138#if !TARGET_OS_EMBEDDED 2139 SystemLoadSystemPowerStateHasChanged( ); 2140#endif 2141 // Get stats on delays while waking up 2142 appStats = (CFArrayRef)_copyRootDomainProperty(CFSTR(kIOPMSleepStatisticsAppsKey)); 2143 if (appStats) { 2144 logASLMessageAppStats(appStats, kPMASLDomainKernelClientStats); 2145 CFRelease(appStats); 2146 } 2147 return; 2148 } 2149 else if (SYSTEM_WILL_WAKE(capArgs) ) 2150 { 2151 // Get stats on delays while going to sleep 2152 appStats = (CFArrayRef)_copyRootDomainProperty(CFSTR(kIOPMSleepStatisticsAppsKey)); 2153 if (appStats) { 2154 logASLMessageAppStats(appStats, kPMASLDomainKernelClientStats); 2155 CFRelease(appStats); 2156 } 2157 gMachineStateRevertible = true; 2158 2159 CFRunLoopPerformBlock(_getPMRunLoop(), kCFRunLoopDefaultMode, 2160 ^{ configAssertionType(kInteractivePushServiceType, false); }); 2161 CFRunLoopWakeUp(_getPMRunLoop()); 2162 2163 deliverCapabilityBits = kIOPMEarlyWakeNotification | 2164 kIOPMCapabilityCPU | kIOPMCapabilityDisk | kIOPMCapabilityNetwork ; 2165 2166 if (IS_CAP_GAIN(capArgs, kIOPMSystemCapabilityGraphics)) 2167 deliverCapabilityBits |= kIOPMCapabilityVideo | kIOPMCapabilityAudio; 2168 2169 sendNoRespNotification( deliverCapabilityBits ); 2170 } 2171 2172 if (capArgs->notifyRef) 2173 IOAllowPowerChange(gRootDomainConnect, capArgs->notifyRef); 2174 2175} 2176 2177/************************************************************************************/ 2178/************************************************************************************/ 2179/************************************************************************************/ 2180/************************************************************************************/ 2181/************************************************************************************/ 2182 2183#pragma mark - 2184#pragma mark Responses 2185 2186static PMResponseWrangler *connectionFireNotification( 2187 int interestBitsNotify, 2188 long kernelAcknowledgementID) 2189{ 2190 int affectedBits = 0; 2191 CFArrayRef interested = NULL; 2192 PMConnection *connection = NULL; 2193 int interestedCount = 0; 2194 uint32_t messageToken = 0; 2195 int calloutCount = 0; 2196 2197 PMResponseWrangler *responseWrangler = NULL; 2198 PMResponse *awaitThis = NULL; 2199 2200 /* 2201 * If a response wrangler is active, store the new notification on the 2202 * active wrangler. Then wait for its completion before firing the new 2203 * notification. Only the last notification is stored. 2204 */ 2205 if (gLastResponseWrangler) 2206 { 2207 gLastResponseWrangler->nextIsValid = true; 2208 gLastResponseWrangler->nextInterestBits = interestBitsNotify; 2209 gLastResponseWrangler->nextKernelAcknowledgementID = kernelAcknowledgementID; 2210 return gLastResponseWrangler; 2211 } 2212 2213 2214 // We only send state change notifications out to entities interested in the changing 2215 // bits, or interested in a subset of the changing bits. 2216 // Any client who is interested in a superset of the changing bits shall not receive 2217 // a notification. 2218 // affectedBits & InterestedBits != 0 2219 // and affectedBits & InterestedBits == InterestedBits 2220 2221 affectedBits = interestBitsNotify ^ gCurrentCapabilityBits; 2222 2223 gCurrentCapabilityBits = interestBitsNotify; 2224 2225 interested = createArrayOfConnectionsWithInterest(affectedBits); 2226 if (!interested) { 2227 goto exit; 2228 } 2229 interestedCount = (int)CFArrayGetCount(interested); 2230 if (0 == interestedCount) { 2231 goto exit; 2232 } 2233 2234 /* Allocate the response wrangler 2235 * 2236 * This object will be allocated & valid for the duration of 2237 * sending out notifications & awaiting responses. 2238 */ 2239 responseWrangler = calloc(1, sizeof(PMResponseWrangler)); 2240 if (!responseWrangler) { 2241 goto exit; 2242 } 2243 responseWrangler->notificationType = interestBitsNotify; 2244 responseWrangler->awaitResponsesTimeoutSeconds = (int)kPMConnectionNotifyTimeoutDefault; 2245 responseWrangler->kernelAcknowledgementID = kernelAcknowledgementID; 2246 2247 2248 /* 2249 * We will track each notification we're sending out with an individual response. 2250 * Record that response in the "active response array" so we can group them 2251 * all later when they acknowledge, or fail to acknowledge. 2252 */ 2253 responseWrangler->awaitingResponses = 2254 CFArrayCreateMutable(kCFAllocatorDefault, interestedCount, &_CFArrayVanillaCallBacks); 2255 responseWrangler->awaitingResponsesCount = interestedCount; 2256 2257 responseWrangler->responseStats = 2258 CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks); 2259 for (calloutCount=0; calloutCount<interestedCount; calloutCount++) 2260 { 2261 connection = (PMConnection *)CFArrayGetValueAtIndex(interested, calloutCount); 2262 2263 if ((MACH_PORT_NULL == connection->notifyPort) || 2264 (false == connection->notifyEnable)) { 2265 continue; 2266 } 2267 2268 /* We generate a messagetoken here, which the notifiee must pass 2269 * back into us when the client acknowledges. 2270 * We note the token in the PMResponse struct. 2271 * 2272 * callCount is incremented by 1 to make sure messageToken is not NULL 2273 */ 2274 messageToken = (interestBitsNotify << 16) 2275 | (calloutCount+1); 2276 2277 // Mark this connection with the responseWrangler that's awaiting its responses 2278 connection->responseHandler = responseWrangler; 2279 2280 _sendMachMessage(connection->notifyPort, 2281 0, 2282 interestBitsNotify, 2283 messageToken); 2284 2285 /* 2286 * Track the response! 2287 */ 2288 awaitThis = calloc(1, sizeof(PMResponse)); 2289 if (!awaitThis) { 2290 goto exit; 2291 } 2292 2293 awaitThis->token = messageToken; 2294 awaitThis->connection = connection; 2295 awaitThis->notificationType = interestBitsNotify; 2296 awaitThis->myResponseWrangler = responseWrangler; 2297 awaitThis->notifiedWhen = CFAbsoluteTimeGetCurrent(); 2298 2299 CFArrayAppendValue(responseWrangler->awaitingResponses, awaitThis); 2300 2301 if (gDebugFlags & kIOPMDebugLogCallbacks) 2302 logASLPMConnectionNotify(awaitThis->connection->callerName, interestBitsNotify ); 2303 2304 } 2305 2306 // TODO: Set off a timer to fire in xx30xx seconds 2307 CFRunLoopTimerContext responseTimerContext = 2308 { 0, (void *)responseWrangler, NULL, NULL, NULL }; 2309 responseWrangler->awaitingResponsesTimeout = 2310 CFRunLoopTimerCreate(0, 2311 CFAbsoluteTimeGetCurrent() + responseWrangler->awaitResponsesTimeoutSeconds, 2312 0.0, 0, 0, responsesTimedOut, &responseTimerContext); 2313 2314 if (responseWrangler->awaitingResponsesTimeout) 2315 { 2316 CFRunLoopAddTimer(CFRunLoopGetCurrent(), 2317 responseWrangler->awaitingResponsesTimeout, 2318 kCFRunLoopDefaultMode); 2319 2320 CFRelease(responseWrangler->awaitingResponsesTimeout); 2321 } 2322 2323exit: 2324 if (interested) 2325 CFRelease(interested); 2326 2327 // Record the active wrangler in a global, then clear when reaped. 2328 if (responseWrangler) 2329 gLastResponseWrangler = responseWrangler; 2330 2331 return responseWrangler; 2332} 2333 2334/*****************************************************************************/ 2335static void sendNoRespNotification( int interestBitsNotify ) 2336{ 2337 2338 CFIndex i, count = 0; 2339 uint32_t messageToken = 0; 2340 PMConnection *connection = NULL; 2341 2342 2343 count = CFArrayGetCount(gConnections); 2344 2345 for (i=0; i<count; i++) 2346 { 2347 connection = (PMConnection *)CFArrayGetValueAtIndex(gConnections, i); 2348 2349 if ((connection->interestsBits & interestBitsNotify) == 0) 2350 continue; 2351 2352 if ((MACH_PORT_NULL == connection->notifyPort) || 2353 (false == connection->notifyEnable)) { 2354 continue; 2355 } 2356 2357 // Set messageToken to 0, to indicate that we are not interested in response 2358 messageToken = 0; 2359 2360 2361 _sendMachMessage(connection->notifyPort, 2362 0, 2363 interestBitsNotify, 2364 messageToken); 2365 2366 2367 if (gDebugFlags & kIOPMDebugLogCallbacks) 2368 logASLPMConnectionNotify(connection->callerName, interestBitsNotify ); 2369 2370 } 2371 2372 2373} 2374 2375/*****************************************************************************/ 2376/*****************************************************************************/ 2377 2378static void responsesTimedOut(CFRunLoopTimerRef timer, void * info) 2379{ 2380 PMResponseWrangler *responseWrangler = (PMResponseWrangler *)info; 2381 PMResponse *one_response = NULL; 2382 2383 CFIndex i, responsesCount = 0; 2384#if !TARGET_OS_EMBEDDED 2385 char appName[32]; 2386 char pidbuf[64]; 2387 2388 pidbuf[0] = 0; 2389#endif 2390 2391 if (!responseWrangler) 2392 return; 2393 2394 // Mark the timer as NULL since it's just fired and autoreleased. 2395 responseWrangler->awaitingResponsesTimeout = NULL; 2396 2397 // Iterate list of awaiting responses, and tattle on anyone who hasn't 2398 // acknowledged yet. 2399 // Artificially mark them as "replied", with their reason being "timed out" 2400 responsesCount = CFArrayGetCount(responseWrangler->awaitingResponses); 2401 for (i=0; i<responsesCount; i++) 2402 { 2403 one_response = (PMResponse *)CFArrayGetValueAtIndex( 2404 responseWrangler->awaitingResponses, i); 2405 if (!one_response) 2406 continue; 2407 if (one_response->replied) 2408 continue; 2409 2410 // Caught a tardy reply 2411 one_response->replied = true; 2412 one_response->timedout = true; 2413 one_response->repliedWhen = CFAbsoluteTimeGetCurrent(); 2414 2415 cacheResponseStats(one_response); 2416 2417#if !TARGET_OS_EMBEDDED 2418 if (isA_CFString(one_response->connection->callerName) && 2419 CFStringGetCString(one_response->connection->callerName, appName, sizeof(appName), kCFStringEncodingUTF8)) 2420 snprintf(pidbuf, sizeof(pidbuf), "%s %s(%d)",pidbuf, appName, one_response->connection->callerPID); 2421 else 2422 snprintf(pidbuf, sizeof(pidbuf), "%s (%d)",pidbuf, one_response->connection->callerPID ); 2423#endif 2424 } 2425 2426 checkResponses(responseWrangler); 2427} 2428 2429 2430/*****************************************************************************/ 2431/*****************************************************************************/ 2432 2433#define kMsgPayloadCount 2 2434 2435typedef struct { 2436 mach_msg_header_t header; 2437 mach_msg_body_t body; 2438 uint32_t payload[kMsgPayloadCount]; 2439} IOPMMessageStructure; 2440 2441static void _sendMachMessage( 2442 mach_port_t port, 2443 mach_msg_id_t msg_id, 2444 uint32_t payload_bits, 2445 uint32_t payload_messagetoken) 2446{ 2447 kern_return_t status; 2448 IOPMMessageStructure msg; 2449 2450 bzero(&msg, sizeof(msg)); 2451 2452 msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, 0); 2453 msg.header.msgh_size = sizeof(msg); 2454 msg.header.msgh_remote_port = port; 2455 msg.header.msgh_local_port = MACH_PORT_NULL; 2456 msg.header.msgh_id = msg_id; 2457 2458 msg.body.msgh_descriptor_count = 0; 2459 2460 msg.payload[0] = payload_bits; 2461 msg.payload[1] = payload_messagetoken; 2462 2463 status = mach_msg(&msg.header, /* msg */ 2464 MACH_SEND_MSG | MACH_SEND_TIMEOUT, /* options */ 2465 msg.header.msgh_size, /* send_size */ 2466 0, /* rcv_size */ 2467 MACH_PORT_NULL, /* rcv_name */ 2468 MACH_MSG_TIMEOUT_NONE, /* timeout */ 2469 MACH_PORT_NULL); /* notify */ 2470 2471 if (status == MACH_SEND_TIMED_OUT) { 2472 mach_msg_destroy(&msg.header); 2473 } 2474 2475 if (status != MACH_MSG_SUCCESS) 2476 { 2477 // Pray for the lost message. 2478 } 2479 2480 return; 2481} 2482 2483static aslmsg describeWakeRequest( 2484 aslmsg m, 2485 pid_t pid, 2486 char *describeType, 2487 CFAbsoluteTime requestedTime, 2488 CFStringRef clientInfoString) 2489{ 2490 static int cnt = 0; 2491 char key[50]; 2492 char value[512]; 2493 2494 if (m == NULL) { 2495 m = new_msg_pmset_log(); 2496 asl_set(m, kPMASLDomainKey, kPMASLDomainClientWakeRequests); 2497 cnt = 0; 2498 } 2499 2500 key[0] = value[0] = 0; 2501 proc_name(pid, value, sizeof(value)); 2502 snprintf(key, sizeof(key), "%s%d", KPMASLWakeReqAppNamePrefix, cnt); 2503 asl_set(m, key, value); 2504 2505 snprintf(key, sizeof(key), "%s%d", kPMASLWakeReqTypePrefix, cnt); 2506 asl_set(m, key, describeType); 2507 2508 snprintf(key, sizeof(key), "%s%d", kPMASLWakeReqTimeDeltaPrefix, cnt); 2509 snprintf(value, sizeof(value), "%.0f", requestedTime - CFAbsoluteTimeGetCurrent()); 2510 asl_set(m, key, value); 2511 2512 if (isA_CFString(clientInfoString) && 2513 (CFStringGetCString(clientInfoString, value, sizeof(value), kCFStringEncodingUTF8))) { 2514 snprintf(key, sizeof(key), "%s%d", kPMASLWakeReqClientInfoPrefix, cnt); 2515 asl_set(m, key, value); 2516 } 2517 cnt++; 2518 2519 return m; 2520} 2521 2522#pragma mark - 2523#pragma mark CheckResponses 2524 2525static bool checkResponses_ScheduleWakeEvents(PMResponseWrangler *wrangler) 2526{ 2527 CFIndex i = 0; 2528 CFIndex responsesCount = 0; 2529 bool complete = true; 2530 PMResponse *oneResponse = NULL; 2531 aslmsg m = NULL; 2532 int chosenReq = -1; 2533 CFAbsoluteTime userWake = 0.0; 2534 CFAbsoluteTime earliestWake = kCFAbsoluteTimeIntervalSince1904; // Invalid value 2535 wakeType_e type = kChooseWakeTypeCount; // Invalid value 2536 CFBooleanRef scheduleEvent = kCFBooleanFalse; 2537 bool userWakeReq = false, ssWakeReq = false; 2538 int reqCnt = 0; 2539 2540 2541#if 0 2542 if (ts_apo) { 2543 CFAbsoluteTime t = CFAbsoluteTimeGetCurrent(); 2544 if (ts_apo > t) t = ts_apo; 2545 describeWakeEvent(NULL, 2546 allWakeEventsString, CFSTR("AutoPowerOffTimer"), t, 2547 NULL); 2548 } 2549#endif 2550 responsesCount = CFArrayGetCount(wrangler->awaitingResponses); 2551 2552 for (i=0; i<responsesCount; i++) 2553 { 2554 oneResponse = (PMResponse *)CFArrayGetValueAtIndex(wrangler->awaitingResponses, i); 2555 2556 if (!oneResponse->replied) { 2557 complete = false; 2558 break; 2559 } 2560 2561 if (BIT_IS_SET(wrangler->notificationType, kIOPMSystemCapabilityCPU)) { 2562 // Don't need to look at wake requests if system is not going to sleep 2563 continue; 2564 } 2565 2566 if (oneResponse->connection == NULL) { 2567 // Requesting process has died. Ignore its request 2568 continue; 2569 } 2570 2571 if (VALID_DATE(oneResponse->maintenanceRequested)) 2572 { 2573 m = describeWakeRequest(m, oneResponse->connection->callerPID, 2574 "Maintenance", 2575 oneResponse->maintenanceRequested, 2576 oneResponse->clientInfoString); 2577 if (oneResponse->maintenanceRequested < earliestWake) 2578 { 2579 earliestWake = oneResponse->maintenanceRequested; 2580 type = kChooseMaintenance; 2581 chosenReq = reqCnt; 2582 } 2583 reqCnt++; 2584 } 2585 2586 if (VALID_DATE(oneResponse->sleepServiceRequested)) 2587 { 2588 m = describeWakeRequest(m, oneResponse->connection->callerPID, 2589 "SleepService", 2590 oneResponse->sleepServiceRequested, 2591 oneResponse->clientInfoString); 2592 2593 if (oneResponse->sleepServiceRequested < earliestWake) 2594 { 2595 earliestWake = oneResponse->sleepServiceRequested; 2596 type = kChooseSleepServiceWake; 2597 chosenReq = reqCnt; 2598 } 2599 ssWakeReq = true; 2600 reqCnt++; 2601 } 2602 2603 if (VALID_DATE(oneResponse->timerPluginRequested)) 2604 { 2605 m = describeWakeRequest(m, oneResponse->connection->callerPID, 2606 "TimerPlugin", 2607 oneResponse->timerPluginRequested, 2608 oneResponse->clientInfoString); 2609 2610 if (oneResponse->timerPluginRequested < earliestWake) 2611 { 2612 earliestWake = oneResponse->timerPluginRequested; 2613 type = kChooseTimerPlugin; 2614 chosenReq = reqCnt; 2615 } 2616 reqCnt++; 2617 } 2618 } 2619 2620 if ( !complete || BIT_IS_SET(wrangler->notificationType, kIOPMSystemCapabilityCPU)) 2621 { 2622 // Either some clients haven't responded yet, or 2623 // system is not going to sleep. So, no need to schedule wake yet 2624 goto exit; 2625 } 2626 2627 if (earliestWake < (CFAbsoluteTimeGetCurrent()+60)) { 2628 // Make sure that wake request is at least 1 min from now 2629 earliestWake = (CFAbsoluteTimeGetCurrent()+60); 2630 } 2631 userWake = getEarliestRequestAutoWake(); 2632 if (VALID_DATE(userWake)) { 2633 m = describeWakeRequest(m, getpid(), "UserWake", userWake, NULL); 2634 if (userWake <= earliestWake) { 2635 earliestWake = userWake; 2636 type = kChooseFullWake; 2637 chosenReq = reqCnt; 2638 if (earliestWake < (CFAbsoluteTimeGetCurrent()+60)) { 2639 // Make sure that wake request is at least 1 min from now 2640 earliestWake = (CFAbsoluteTimeGetCurrent()+60); 2641 } 2642 } 2643 userWakeReq = true; 2644 reqCnt++; 2645 } 2646 2647 if (ts_apo != 0) { 2648 // Report existence of user wake request or SS request to IOPPF(thru rootDomain) 2649 // This is used in figuring out if Auto Power Off should be scheduled 2650 if (userWakeReq || ssWakeReq) { 2651 scheduleEvent = kCFBooleanTrue; 2652 } 2653 _setRootDomainProperty(CFSTR(kIOPMUserWakeAlarmScheduledKey), scheduleEvent); 2654 } 2655 2656 if (m != NULL) { 2657 char chosenStr[5]; 2658 snprintf(chosenStr, sizeof(chosenStr), "%d", chosenReq); 2659 asl_set(m, kPMASLWakeReqChosenIdx, chosenStr); 2660 asl_send(NULL, m); 2661 } 2662 2663 PMScheduleWakeEventChooseBest(earliestWake, type); 2664 2665exit: 2666 2667 if (m != NULL) { 2668 asl_release(m); 2669 } 2670 return complete; 2671} 2672 2673static void checkResponses(PMResponseWrangler *wrangler) 2674{ 2675 2676 2677 if (!checkResponses_ScheduleWakeEvents(wrangler)) { 2678 // Not all clients acknowledged. 2679 return; 2680 } 2681 2682 if (wrangler->responseStats) { 2683 logASLMessageAppStats(wrangler->responseStats, kPMASLDomainPMClientStats); 2684 CFRelease(wrangler->responseStats); 2685 wrangler->responseStats = NULL; 2686 } 2687 2688 // Completion: all clients have acknowledged. 2689 if (wrangler->awaitingResponsesTimeout) { 2690 CFRunLoopTimerInvalidate(wrangler->awaitingResponsesTimeout); 2691 wrangler->awaitingResponsesTimeout = NULL; 2692 } 2693 2694 // Handle PowerManagement acknowledgements 2695 if (wrangler->kernelAcknowledgementID) 2696 { 2697 IOAllowPowerChange(gRootDomainConnect, wrangler->kernelAcknowledgementID); 2698 } 2699 2700 cleanupResponseWrangler(wrangler); 2701 2702 return; 2703} 2704 2705 2706static void PMScheduleWakeEventChooseBest(CFAbsoluteTime scheduleTime, wakeType_e type) 2707{ 2708 CFStringRef scheduleWakeType = NULL; 2709 CFNumberRef diff_secs = NULL; 2710 uint64_t secs_to_apo = ULONG_MAX; 2711 CFAbsoluteTime cur_time = 0.0; 2712 uint32_t sleepType = kIOPMSleepTypeInvalid; 2713 bool skip_scheduling = false; 2714 CFBooleanRef scheduleEvent = kCFBooleanFalse; 2715 2716 2717 if (!VALID_DATE(scheduleTime)) 2718 { 2719 // Set a huge number for following comaprisions with power off timer 2720 scheduleTime = kCFAbsoluteTimeIntervalSince1904; 2721 } 2722 2723 2724 // Always set the Auto Power off timer 2725 if ( ts_apo != 0 ) 2726 { 2727 if (ts_apo < scheduleTime+kAutoPowerOffSleepAhead) { 2728 2729 getPlatformSleepType( &sleepType ); 2730 if ((scheduleEvent == kCFBooleanFalse) && (sleepType == kIOPMSleepTypePowerOff)) 2731 skip_scheduling = true; 2732 2733 } 2734 2735 cur_time = CFAbsoluteTimeGetCurrent(); 2736 if (cur_time >= ts_apo) 2737 secs_to_apo = 0; 2738 else 2739 secs_to_apo = ts_apo - cur_time; 2740 2741 diff_secs = CFNumberCreate(0, kCFNumberLongType, &secs_to_apo); 2742 if (diff_secs) { 2743 _setRootDomainProperty( CFSTR(kIOPMAutoPowerOffTimerKey), (CFTypeRef)diff_secs ); 2744 CFRelease(diff_secs); 2745 } 2746 2747 if ( skip_scheduling ) { 2748 // Auto Power off timer is the earliest one and system is capable of 2749 // entering ErpLot6. Don't schedule any other wakes. 2750 if (gDebugFlags & kIOPMDebugLogCallbacks) 2751 asl_log(0,0,ASL_LEVEL_ERR, 2752 "Not scheduling other wakes to allow AutoPower off. APO timer:%lld\n", 2753 secs_to_apo); 2754 return; 2755 } 2756 if (gDebugFlags & kIOPMDebugLogCallbacks) 2757 asl_log(0,0,ASL_LEVEL_ERR, "sleepType:%d APO timer:%lld secs\n", 2758 sleepType, secs_to_apo); 2759 } 2760 2761 if ( scheduleTime == kCFAbsoluteTimeIntervalSince1904) 2762 { 2763 // Nothing to schedule. bail.. 2764 return; 2765 } 2766 2767 // INVARIANT: At least one of the WakeTimes we're evaluating has a valid date 2768 if ((kChooseMaintenance == type) || (kChooseTimerPlugin == type)) 2769 { 2770 scheduleWakeType = CFSTR(kIOPMMaintenanceScheduleImmediate); 2771 } else if (kChooseFullWake == type) 2772 { 2773 scheduleWakeType = CFSTR(kIOPMAutoWakeScheduleImmediate); 2774 } else if (kChooseSleepServiceWake == type) 2775 { 2776 scheduleWakeType = CFSTR(kIOPMSleepServiceScheduleImmediate); 2777 } 2778 2779 if (VALID_DATE(scheduleTime) && scheduleWakeType) 2780 { 2781 CFDateRef theChosenDate = NULL; 2782 2783 if ((theChosenDate = CFDateCreate(0, scheduleTime))) 2784 { 2785 /* Tell the RTC when PM wants to be woken up */ 2786 IOPMSchedulePowerEvent(theChosenDate, NULL, scheduleWakeType); 2787 CFRelease(theChosenDate); 2788 2789 } 2790 } 2791 2792 return ; 2793} 2794 2795 2796/*****************************************************************************/ 2797/*****************************************************************************/ 2798 2799static CFArrayRef createArrayOfConnectionsWithInterest( 2800 int interestBits) 2801{ 2802 CFMutableArrayRef arrayFoundInterests = NULL; 2803 PMConnection *lookee; 2804 CFIndex connectionsCount; 2805 CFIndex i; 2806 2807 2808 if (0 == interestBits) 2809 return NULL; 2810 2811 // ********* 2812 2813 arrayFoundInterests = CFArrayCreateMutable(kCFAllocatorDefault, 0, &_CFArrayConnectionCallBacks); 2814 2815 connectionsCount = CFArrayGetCount(gConnections); 2816 2817 for (i=0; i<connectionsCount; i++) 2818 { 2819 lookee = (PMConnection *)CFArrayGetValueAtIndex(gConnections, i); 2820 2821 // Matching interest in this connection 2822 if (interestBits & lookee->interestsBits) { 2823 CFArrayAppendValue(arrayFoundInterests, lookee); 2824 } 2825 } 2826 2827 return arrayFoundInterests; 2828 2829} 2830 2831/*****************************************************************************/ 2832/*****************************************************************************/ 2833 2834static IOReturn createConnectionWithID(PMConnection **out) 2835{ 2836 static bool hasLoggedTooManyConnections = false; 2837 2838 if ((globalConnectionIDTally > kMaxConnectionIDCount) 2839 && !hasLoggedTooManyConnections) 2840 { 2841 // ASL_LOG: KEEP FOR NOW 2842// asl_log(NULL, NULL, ASL_LEVEL_ERR, "PM configd connections: connection count exceeded %d", 2843// kMaxConnectionIDCount); 2844 return kIOReturnNoSpace; 2845 } 2846 2847 *out = (PMConnection *)calloc(1, sizeof(PMConnection)); 2848 2849 if (!*out) 2850 return kIOReturnNoMemory; 2851 2852 ((PMConnection *)*out)->uniqueID = kConnectionOffset + globalConnectionIDTally++; 2853 2854 // Add new connection to the global tracking array 2855 CFArrayAppendValue(gConnections, *out); 2856 2857 return kIOReturnSuccess; 2858} 2859 2860/*****************************************************************************/ 2861/*****************************************************************************/ 2862 2863static PMConnection *connectionForID(uint32_t findMe) 2864{ 2865 CFIndex where = 0; 2866 CFRange theRange = CFRangeMake(0, CFArrayGetCount(gConnections)); 2867 PMConnection dummy; 2868 2869 // Our gConnections CFArray equality callback only checks the "uniqueID" field 2870 // of the passed in PMConnection type. Searching for the value "dummy" 2871 // will return the first instance with connection ID "findMe". 2872 2873 dummy.uniqueID = findMe; 2874 where = CFArrayGetFirstIndexOfValue(gConnections, theRange, &dummy); 2875 2876 if (kCFNotFound == where) { 2877 return NULL; 2878 } else { 2879 return (PMConnection *)CFArrayGetValueAtIndex(gConnections, where); 2880 } 2881} 2882 2883// Unclamps machine from SilentRunning if the machine is currently clamped. 2884// Does so by going through rootDomain's setProperties implementation. 2885// RootDomain informs all interested drivers (including AppleSMC) that 2886// SilentRunning has been turned off 2887__private_extern__ IOReturn _unclamp_silent_running(bool sendNewCapBits) 2888{ 2889 IOPMCapabilityBits deliverCapabilityBits; 2890 2891 // Nothing to do. It's already unclamped 2892 if(gCurrentSilentRunningState == kSilentRunningOff) 2893 return kIOReturnSuccess; 2894 2895 // Nothing to do. SMC doesn't support SR 2896 if(!smcSilentRunningSupport()) 2897 return kIOReturnUnsupported; 2898 2899 // Unset background, push and silent bits only if they are set in current capabilities. Inform PMConnection clients 2900 if ((sendNewCapBits) && (gCurrentCapabilityBits & 2901 (kIOPMCapabilityPushServiceTask|kIOPMCapabilityBackgroundTask|kIOPMCapabilitySilentRunning))) 2902 { 2903 deliverCapabilityBits = gCurrentCapabilityBits & ~(kIOPMCapabilitySilentRunning); 2904 connectionFireNotification(deliverCapabilityBits, 0); 2905 } 2906 2907 int newSRCap = kSilentRunningOff; 2908 CFNumberRef num = CFNumberCreate(0, kCFNumberIntType, &newSRCap); 2909 if (num) 2910 { 2911 if(rootDomainService) 2912 { 2913 IORegistryEntrySetCFProperty(rootDomainService, 2914 CFSTR(kIOPMSilentRunningKey), 2915 num); 2916 gCurrentSilentRunningState = kSilentRunningOff; 2917 } 2918 else 2919 { 2920 CFRelease(num); 2921 return kIOReturnInternalError; 2922 } 2923 CFRelease(num); 2924 return kIOReturnSuccess; 2925 } 2926 else 2927 { 2928 return kIOReturnInternalError; 2929 } 2930} 2931 2932__private_extern__ void _set_sleep_revert(bool state) 2933{ 2934 gMachineStateRevertible = state; 2935} 2936 2937__private_extern__ bool _can_revert_sleep(void) 2938{ 2939 return gMachineStateRevertible; 2940} 2941 2942 2943__private_extern__ io_connect_t getRootDomainConnect() 2944{ 2945 return gRootDomainConnect; 2946} 2947 2948/* 2949 * Returns the per-platform sleep service cap timeout for the current power 2950 * source in milliseconds 2951 */ 2952#if !TARGET_OS_EMBEDDED 2953__private_extern__ int getCurrentSleepServiceCapTimeout() 2954{ 2955 static int acCapTimeout = -1; 2956 static int battCapTimeout = -1; 2957 int currentCap = 3*60*1000; // Set this to a default value of 3mins 2958 int pwrSrc = _getPowerSource(); 2959 2960 if(kACPowered == pwrSrc && acCapTimeout != (-1)) 2961 return acCapTimeout; 2962 else if(kBatteryPowered == pwrSrc && battCapTimeout != (-1)) 2963 return battCapTimeout; 2964 2965 CFDictionaryRef dwServicesDict = NULL; 2966 CFDictionaryRef ssModesDict = NULL; 2967 CFDictionaryRef modeADict = NULL; 2968 CFDictionaryRef baseIntervalsDict = NULL; 2969 CFDictionaryRef powerSourceDict = NULL; 2970 CFNumberRef baseWakeCap = NULL; 2971 2972 dwServicesDict = (CFDictionaryRef)_copyRootDomainProperty( 2973 CFSTR("DarkWakeServices")); 2974 if(dwServicesDict) { 2975 ssModesDict = CFDictionaryGetValue( 2976 dwServicesDict, 2977 CFSTR("SleepServicesModes")); 2978 if(ssModesDict) { 2979 modeADict = CFDictionaryGetValue( 2980 ssModesDict, 2981 CFSTR("ModeA")); 2982 if(modeADict) { 2983 baseIntervalsDict = CFDictionaryGetValue( 2984 modeADict, 2985 CFSTR("BaseIntervals")); 2986 if(baseIntervalsDict) { 2987 if(kACPowered == pwrSrc) { 2988 powerSourceDict = CFDictionaryGetValue( 2989 baseIntervalsDict, 2990 CFSTR("AC")); 2991 if(powerSourceDict) 2992 baseWakeCap = CFDictionaryGetValue( 2993 powerSourceDict, 2994 CFSTR("BaseWakeCapInterval")); 2995 if(baseWakeCap) { 2996 CFNumberGetValue( 2997 baseWakeCap, 2998 kCFNumberIntType, 2999 &acCapTimeout); 3000 // (Time in secs) * 1000 3001 acCapTimeout *= 1000; 3002 currentCap = acCapTimeout; 3003 } else { 3004 acCapTimeout = battCapTimeout = -1; 3005 } 3006 } else if (kBatteryPowered == pwrSrc) { 3007 powerSourceDict = CFDictionaryGetValue( 3008 baseIntervalsDict, 3009 CFSTR("Battery")); 3010 if(powerSourceDict) 3011 baseWakeCap = CFDictionaryGetValue( 3012 powerSourceDict, 3013 CFSTR("BaseWakeCapInterval")); 3014 if(baseWakeCap) { 3015 CFNumberGetValue( 3016 baseWakeCap, 3017 kCFNumberIntType, 3018 &battCapTimeout); 3019 // (Time in secs) * 1000 3020 battCapTimeout *= 1000; 3021 currentCap = battCapTimeout; 3022 } else { 3023 acCapTimeout = battCapTimeout = -1; 3024 } 3025 } 3026 } else { 3027 acCapTimeout = battCapTimeout = -1; 3028 } 3029 } else { 3030 acCapTimeout = battCapTimeout = -1; 3031 } 3032 } else { 3033 acCapTimeout = battCapTimeout = -1; 3034 } 3035 CFRelease(dwServicesDict); 3036 } else { 3037 acCapTimeout = battCapTimeout = -1; 3038 } 3039 3040 return currentCap; 3041} 3042#endif 3043 3044 3045