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