1/*
2 * Copyright (c) 2008 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_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. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <sys/types.h>
25#include <sys/sysctl.h>
26#include <notify.h>
27#include <IOKit/hidsystem/IOHIDLib.h>
28
29#include "PrivateLib.h"
30#include "SystemLoad.h"
31#include "PMStore.h"
32#include "PMAssertions.h"
33#include "PMSettings.h"
34#include "PMConnection.h"
35#include "Platform.h"
36
37#ifndef  kIOHIDSystemUserHidActivity
38#define kIOHIDSystemUserHidActivity    iokit_family_msg(sub_iokit_hidsystem, 6)
39#endif
40
41#define IDLE_HID_ACTIVITY_SECS ((uint64_t)(5*60))
42static int minOfThree(int a, int b, int c);
43
44// Forwards
45const bool  kNoNotify  = false;
46const bool  kYesNotify = true;
47static void shareTheSystemLoad(bool shouldNotify);
48
49
50// Globals
51static CFStringRef  systemLoadKey = NULL;
52static CFStringRef  systemLoadDetailedKey = NULL;
53//  The following  bool variables are implicit arguments to
54//  the function shareTheSystemLoad()
55static bool   onACPower                 = FALSE;
56static bool   onBatteryPower            = FALSE;
57static bool   batteryBelowThreshold     = FALSE;
58
59static bool   coresConstrained          = FALSE;
60static bool   forcedIdle                = FALSE;
61static bool   plimitBelowThreshold      = FALSE;
62static bool   thermalWarningLevel       = FALSE;
63
64static bool   displayIsOff                = FALSE;
65static bool   displaySleepEnabled       = FALSE;
66
67static int    gNotifyToken              = 0;
68
69
70/*! UserActiveStruct records the many data sources that affect
71 *  our concept of user-is-active; and the user's activity level.
72 *
73 *  Track every aspect of "user is active on the system" in this struct
74 *  right here.
75 *  Anyplace in powerd that we consider "is the user active"; that
76 *  data needs to go through UserActiveStruct and the functions that
77 *  operate on it.
78 */
79typedef struct {
80    int     token;
81    bool    assertionsActive;
82    dispatch_source_t timer;
83
84    /*! presentActive: user has been present and active within 5 minutes
85     */
86    bool    presentActive;
87
88    /*! hidActive returns true when HID has seen received a HID packet
89     *  within < 5 minutes.
90     *  IOHIDFamily/IOHIDSystem.cpp implements this 5 minute timeout
91     *  using constant IDLE_HID_ACTIVITY_NSECS */
92    bool    hidActive;
93
94    /*! loggedIn is true if there's a console user logged in to
95     *  loginWindow. Also returns true for ScreenShared users.
96     */
97    bool    loggedIn;
98
99    /*! rootDomain tracks the IOPMrootDomain's concept of user activity, as
100     *  decided by root domain's policy surrounding kStimulusUserIsActive
101     *  and kStimulusUserIsInactive.
102     *  By definition, rootDomain is true when the system is in S0 notification
103     *  wake, and set to false on asleep.
104     */
105    bool    rootDomain;
106
107    /*! sleepFromUserWakeTime is a timestamp tracking the last time the system
108     *  was in full S0 user wake, and it went to sleep.
109     *  We will not update this timestamp on maintenance wakes.
110     */
111    CFAbsoluteTime  sleepFromUserWakeTime;
112
113    /*! postedLevels is the last set of user activity levels we've published.
114     *  This corresponds to the currently available return value to
115     *  processes calling IOPMGetUserActivityLevel().
116     */
117    uint64_t postedLevels;
118} UserActiveStruct;
119
120static UserActiveStruct userActive;
121
122/************************* ****************************** ********************/
123
124static void updateUserActivityLevels(void);
125
126static void userActive_prime(void) {
127    bzero(&userActive, sizeof(UserActiveStruct));
128
129    userActive.postedLevels = 0xFFFF; // bogus value
130}
131
132bool userActiveRootDomain(void)
133{
134    return userActive.rootDomain;
135}
136void userActiveHandleSleep(void)
137{
138    if (userActive.rootDomain) {
139        userActive.sleepFromUserWakeTime = CFAbsoluteTimeGetCurrent();
140    }
141    userActive.rootDomain = false;
142}
143
144void userActiveHandlePowerAssertionsChanged()
145{
146    updateUserActivityLevels();
147}
148
149
150void userActiveHandleRootDomainActivity(void)
151{
152    CFBooleanRef    userIsActive = NULL;
153
154    userIsActive = IORegistryEntryCreateCFProperty(getRootDomain(),
155                                                   CFSTR(kIOPMUserIsActiveKey),
156                                                   0, 0);
157    if (userIsActive == kCFBooleanTrue) {
158        _unclamp_silent_running(true);
159        cancel_NotificationDisplayWake();
160        cancelPowerNapStates();
161#if TCPKEEPALIVE
162        cancelTCPKeepAliveExpTimer();
163#endif
164        userActive.rootDomain = true;
165    }
166
167    if (userIsActive) {
168        CFRelease(userIsActive);
169    }
170}
171
172void updateUserActivityLevels(void)
173{
174    static int          token = 0;
175    uint64_t            levels = 0;
176
177
178    if (userActive.presentActive) {
179        levels |= kIOPMUserPresentActive;
180    }
181    if (checkForActivesByType(kPreventDisplaySleepType)) {
182        levels |= kIOPMUserPresentPassive;
183    }
184    if (checkForActivesByType(kNetworkAccessType)) {
185        levels |= kIOPMUserRemoteClientActive;
186    }
187    if (checkForActivesByType(kTicklessDisplayWakeType)) {
188        levels |= kIOPMUserNotificationActive;
189    }
190
191    if (0 == token) {
192        notify_register_check("com.apple.system.powermanagement.useractivity2",
193                              &token);
194    }
195    if (userActive.postedLevels != levels) {
196        notify_set_state(token, levels);
197        notify_post("com.apple.system.powermanagement.useractivity2");
198        userActive.postedLevels = levels;
199    }
200}
201
202
203/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
204/*! PresentActive user detector
205 */
206static void updateUserPresentActive( )
207{
208#if !TARGET_OS_EMBEDDED
209   bool presentActive = false;
210
211    if (userActive.assertionsActive
212        || userActive.hidActive)
213    {
214        presentActive = true;
215    }
216
217    if (!userActive.loggedIn || displayIsOff) {
218        presentActive = false;
219    }
220
221    if (presentActive != userActive.presentActive) {
222       if (presentActive) {
223           /* new PresentActive == true */
224           notify_set_state(userActive.token, (uint64_t)kIOUserIsActive);
225       }
226       else  {
227           /* new PresentActive == false */
228           notify_set_state(userActive.token, (uint64_t)kIOUserIsIdle);
229       }
230
231       notify_post(kIOUserActivityNotifyName);
232
233       userActive.presentActive = presentActive;
234
235       updateUserActivityLevels();
236    }
237
238#endif
239
240}
241
242CFAbsoluteTime get_SleepFromUserWakeTime(void)
243{
244    return userActive.sleepFromUserWakeTime;
245}
246
247
248
249/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
250
251static void shareTheSystemLoad(bool shouldNotify)
252{
253    static uint64_t         lastSystemLoad  = 0;
254    uint64_t                theseSystemLoad = 0;
255    int                     userLevel       = kIOSystemLoadAdvisoryLevelGreat;
256    int                     batteryLevel    = kIOSystemLoadAdvisoryLevelGreat;
257    int                     powerLevel      = kIOSystemLoadAdvisoryLevelGreat;
258    int                     combinedLevel   = kIOSystemLoadAdvisoryLevelGreat;
259
260/******************************************
261 * Power Level Computation code begins here
262 * Edit this block of code to change what
263 * defines a "good time" to do work, based on system load.
264 */
265/******************************************/
266
267    if (onACPower) {
268        batteryLevel = kIOSystemLoadAdvisoryLevelGreat;
269    } else if (!batteryBelowThreshold) {
270        batteryLevel = kIOSystemLoadAdvisoryLevelOK;
271    } else {
272        batteryLevel = kIOSystemLoadAdvisoryLevelBad;
273    }
274
275    if (plimitBelowThreshold) {
276        powerLevel = kIOSystemLoadAdvisoryLevelOK;
277    }
278    if (coresConstrained || forcedIdle || thermalWarningLevel) {
279        powerLevel = kIOSystemLoadAdvisoryLevelBad;
280    }
281
282    // TODO: Use seconds since last UI activity as an indicator of
283    // userLevel. Basing this data on display dimming is a crutch,
284    // and may be invalid on systems with display dimming disabled.
285    if (userActive.loggedIn) {
286        if (displayIsOff)
287        {
288            if (_DWBT_enabled()) {
289               // System allows DWBT & user has opted in
290
291               if (isA_BTMtnceWake( ) )
292                  userLevel = kIOSystemLoadAdvisoryLevelGreat;
293               else
294                  userLevel = kIOSystemLoadAdvisoryLevelOK;
295            }
296            else
297               userLevel = kIOSystemLoadAdvisoryLevelGreat;
298
299        } else {
300            userLevel = kIOSystemLoadAdvisoryLevelOK;
301        }
302        // TODO: If user is performing a full screen activity, or
303        // is actively producing UI events, time is BAD.
304    }
305
306    // The combined level is the lowest/worst level of the contributing factors
307    combinedLevel = minOfThree(userLevel, batteryLevel, powerLevel);
308
309/******************************************/
310/* Power Level Computation code ends here */
311/******************************************/
312
313    theseSystemLoad = combinedLevel
314                | (userLevel << 8)
315                | (batteryLevel << 16)
316                | (powerLevel << 24);
317
318    if (theseSystemLoad != lastSystemLoad)
319    {
320        CFMutableDictionaryRef publishDetails = NULL;
321        CFNumberRef publishNum = NULL;
322
323        lastSystemLoad = theseSystemLoad;
324
325        /* Publish the combinedLevel under our notify key 'kIOSystemLoadAdvisoryNotifyName'
326         */
327        notify_set_state(gNotifyToken, (uint64_t)combinedLevel);
328
329        /* Publish the SystemLoad key read by API
330         * IOGetSystemLoadAdvisory();
331         */
332        publishNum = CFNumberCreate(0, kCFNumberSInt64Type, &theseSystemLoad);
333        if (publishNum)
334        {
335            PMStoreSetValue(systemLoadKey, publishNum);
336            CFRelease(publishNum);
337            publishNum = NULL;
338        }
339
340        /* Publish the Detailed key read by API
341         * CFDictionaryRef IOPMCheckSystemLoadDetailed();
342         */
343        publishDetails = CFDictionaryCreateMutable(0, 0,
344                                &kCFTypeDictionaryKeyCallBacks,
345                                &kCFTypeDictionaryValueCallBacks);
346        if (!publishDetails) return;
347        publishNum = CFNumberCreate(0, kCFNumberIntType, &userLevel);
348        if (publishNum) {
349            CFDictionarySetValue(publishDetails,
350                                    kIOSystemLoadAdvisoryUserLevelKey,
351                                    publishNum);
352            CFRelease(publishNum);
353            publishNum = 0;
354        }
355        publishNum = CFNumberCreate(0, kCFNumberIntType, &batteryLevel);
356        if (publishNum) {
357            CFDictionarySetValue(publishDetails,
358                                    kIOSystemLoadAdvisoryBatteryLevelKey,
359                                    publishNum);
360            CFRelease(publishNum);
361            publishNum = 0;
362        }
363        publishNum = CFNumberCreate(0, kCFNumberIntType, &powerLevel);
364        if (publishNum) {
365            CFDictionarySetValue(publishDetails,
366                                    kIOSystemLoadAdvisoryThermalLevelKey,
367                                    publishNum);
368            CFRelease(publishNum);
369            publishNum = 0;
370        }
371        publishNum = CFNumberCreate(0, kCFNumberIntType, &combinedLevel);
372        if (publishNum) {
373            CFDictionarySetValue(publishDetails,
374                                    kIOSystemLoadAdvisoryCombinedLevelKey,
375                                    publishNum);
376            CFRelease(publishNum);
377            publishNum = 0;
378        }
379
380        // Publish SystemLoadDetailed
381        PMStoreSetValue(systemLoadDetailedKey, publishDetails);
382        CFRelease(publishDetails);
383
384        // post notification
385        if (shouldNotify) {
386            notify_post(kIOSystemLoadAdvisoryNotifyName);
387        }
388    }
389}
390
391
392/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
393static void
394hidActivityStateChange(void *ref, io_service_t service, natural_t messageType, void *arg)
395{
396
397   if (messageType != kIOHIDSystemUserHidActivity)
398      return;
399
400
401   userActive.hidActive = ((uint64_t)arg) ? false : true;
402
403    updateUserPresentActive();
404}
405/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
406static void hidSystemMatched(
407    void *note_port_in,
408    io_iterator_t iter)
409{
410    IONotificationPortRef       note_port = (IONotificationPortRef)note_port_in;
411    io_service_t                hidSystem  = MACH_PORT_NULL;
412    io_object_t                 notification_object = MACH_PORT_NULL;
413    mach_port_t                 connect = MACH_PORT_NULL;
414
415    if((hidSystem = (io_registry_entry_t)IOIteratorNext(iter)))
416    {
417        IOServiceAddInterestNotification(
418                    note_port,
419                    hidSystem,
420                    kIOGeneralInterest,
421                    hidActivityStateChange,
422                    NULL,
423                    &notification_object);
424
425        IOServiceOpen(hidSystem, mach_task_self(), kIOHIDParamConnectType, &connect);
426        if (connect) {
427            bool hidIdle;
428            IOHIDGetActivityState(connect, &hidIdle);
429            userActive.hidActive = !hidIdle;
430
431            IOServiceClose(connect);
432            updateUserPresentActive();
433        }
434
435        IOObjectRelease(hidSystem);
436    }
437
438}
439
440
441/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
442
443__private_extern__ void SystemLoad_prime(void)
444{
445    IONotificationPortRef       notify_port = 0;
446    io_iterator_t               hid_iter = 0;
447    kern_return_t               kr;
448    CFRunLoopSourceRef          rlser = 0;
449
450    userActive_prime();
451
452    systemLoadKey = SCDynamicStoreKeyCreate(
453                    kCFAllocatorDefault,
454                    CFSTR("%@%@"),
455                    kSCDynamicStoreDomainState,
456                    CFSTR("/IOKit/PowerManagement/SystemLoad"));
457
458    systemLoadDetailedKey = SCDynamicStoreKeyCreate(
459                    kCFAllocatorDefault,
460                    CFSTR("%@%@"),
461                    kSCDynamicStoreDomainState,
462                    CFSTR("/IOKit/PowerManagement/SystemLoad/Detailed"));
463
464    notify_register_check(kIOSystemLoadAdvisoryNotifyName, &gNotifyToken);
465
466    notify_register_check(kIOUserActivityNotifyName, &userActive.token);
467    notify_set_state(userActive.token, (uint64_t)kIOUserIsActive);
468
469    // If this is a desktop, then we won't get any battery notifications.
470    // Let's prime the battery pump right here with an initial coll.
471    SystemLoadBatteriesHaveChanged(_batteries());
472
473    SystemLoadPrefsHaveChanged();
474
475#if !TARGET_OS_EMBEDDED
476    SystemLoadUserStateHasChanged();
477#endif
478
479    SystemLoadCPUPowerHasChanged(NULL);
480
481    notify_port = IONotificationPortCreate(0);
482    rlser = IONotificationPortGetRunLoopSource(notify_port);
483    if(rlser)
484       CFRunLoopAddSource(CFRunLoopGetCurrent(), rlser, kCFRunLoopDefaultMode);
485
486    kr = IOServiceAddMatchingNotification(
487                                notify_port,
488                                kIOFirstMatchNotification,
489                                IOServiceMatching("IOHIDSystem"),
490                                hidSystemMatched,
491                                (void *)notify_port,
492                                &hid_iter);
493    if(KERN_SUCCESS == kr)
494    {
495        // Install notifications on existing instances.
496        hidSystemMatched((void *)notify_port, hid_iter);
497    }
498    else {
499        asl_log(NULL, NULL, ASL_LEVEL_ERR,
500                "Failed to match HIDSystem(0x%x)\n", kr);
501    }
502
503
504
505}
506
507
508/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
509
510/* @function SystemLoadDisplayPowerStateHasChanged
511 * @param displayIsOn is true if the backlight is powered
512 * Populates:
513 *      displayIsOff
514 */
515__private_extern__ void SystemLoadDisplayPowerStateHasChanged(bool _displayIsOff)
516{
517    if (displayIsOff == _displayIsOff) {
518        return;
519    }
520
521    displayIsOff = _displayIsOff;
522
523    shareTheSystemLoad(kYesNotify);
524    updateUserPresentActive();
525}
526
527/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
528
529/* @function SystemLoadPrefsHaveChanged
530 * @abstract We check whether DIsplay Sleep Timer == 0
531 * Populates:
532 *      displaySleepEnabled
533 */
534__private_extern__ void SystemLoadPrefsHaveChanged(void)
535{
536    SCDynamicStoreRef   _store       = _getSharedPMDynamicStore();
537    CFDictionaryRef     liveSettings = NULL;
538    CFNumberRef         displaySleep = NULL;
539    CFTypeRef           dwbt = NULL;
540    int                 idle, newDWBT;
541    static bool         lastDisplaySleep = false;
542    static int          lastDWBT = -1;
543    bool                notify = false;
544
545    liveSettings = SCDynamicStoreCopyValue(_store,
546                        CFSTR(kIOPMDynamicStoreSettingsKey));
547    if (liveSettings)
548    {
549        displaySleep = CFDictionaryGetValue(liveSettings,
550                                    CFSTR(kIOPMDisplaySleepKey));
551        if (displaySleep)
552        {
553            CFNumberGetValue(displaySleep, kCFNumberIntType, &idle);
554
555            displaySleepEnabled = idle ? true:false;
556
557            if (displaySleepEnabled != lastDisplaySleep)
558            {
559                lastDisplaySleep = displaySleepEnabled;
560                notify = true;
561            }
562        }
563        dwbt = CFDictionaryGetValue(liveSettings,
564                                    CFSTR(kIOPMDarkWakeBackgroundTaskKey));
565        if (dwbt)
566        {
567            newDWBT = CFBooleanGetValue(dwbt) ? 1 : 0;
568            if (lastDWBT != newDWBT)
569            {
570                lastDWBT = newDWBT;
571                notify = true;
572            }
573        }
574        CFRelease(liveSettings);
575    }
576
577    if (notify)
578        shareTheSystemLoad(kYesNotify);
579    return;
580}
581
582/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
583
584/* @function SystemLoadBatteriesHaveChanged
585 * Populates:
586 *   onACPower
587 *   onBatteryPower
588 *   batteryBelowThreshold
589 */
590__private_extern__ void SystemLoadBatteriesHaveChanged(IOPMBattery **batt_stats)
591{
592    static const int kBatThreshold = 40;
593    int count = _batteryCount();
594    int sumMax = 0;
595    int sumCurrent = 0;
596    int i;
597
598    onACPower = false;
599    onBatteryPower = false;
600    batteryBelowThreshold = false;
601
602    if (0 == count)
603    {
604        onACPower = true;
605        goto exit;
606    }
607    for (i=0; i<count; i++)
608    {
609        if( batt_stats[i]->externalConnected ) {
610            onACPower = true;
611        }
612        sumCurrent += batt_stats[i]->currentCap;
613        sumMax += batt_stats[i]->maxCap;
614    }
615    if (!onACPower)
616    {
617        onBatteryPower = true;
618    }
619    if (sumMax
620        && (kBatThreshold > (100*sumCurrent)/sumMax))
621    {
622        batteryBelowThreshold = true;
623    }
624
625exit:
626    shareTheSystemLoad(kYesNotify);
627}
628
629/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
630
631/* @function SystemLoadCPUPowerHasChanged
632 * Populates:
633 *  coresConstrained
634 *  forcedIdle
635 *  plimitBelowThreshold
636 *  thermalWarningLevel
637 */
638__private_extern__ void SystemLoadCPUPowerHasChanged(CFDictionaryRef newCPU)
639{
640    CFDictionaryRef ourAllocatedCPU = NULL;
641    CFNumberRef     plimitNum = NULL;
642    int             plimit = 100;   // defaults to OK value
643    CFNumberRef     cpuCountNum = NULL;
644    int             cpuCount = 0;
645    static int      maxCPUCount = 0;
646    CFNumberRef     runnableTimeNum = NULL;
647    int             runnableTime = 100; // defaults to OK value
648    uint32_t        getlevel = 0;
649    IOReturn        ret;
650
651    if (0 == maxCPUCount) {
652        int  result;
653        size_t len = sizeof(maxCPUCount);
654        result = sysctlbyname("hw.ncpu", &maxCPUCount, &len, 0, 0);
655        if (-1 == result) { // error
656            maxCPUCount = 0;
657        }
658    }
659
660   // cpuCount defaults to max CPU count
661    cpuCount = maxCPUCount;
662
663    /**/
664    ret = IOPMGetThermalWarningLevel(&getlevel);
665    if ( (kIOReturnSuccess == ret)
666        && (kIOPMThermalWarningLevelNormal != getlevel))
667    {
668        thermalWarningLevel = true;
669    } else {
670        thermalWarningLevel = false;
671    }
672
673    /**/
674    coresConstrained = false;
675    forcedIdle = false;
676    plimitBelowThreshold = false;
677
678    /**/
679    // If caller passed in a NULL CPU dictionary, we'll allocate one here,
680    // and release it at exit.
681    if (!newCPU) {
682        ret = IOPMCopyCPUPowerStatus(&ourAllocatedCPU);
683        if (kIOReturnSuccess == ret) {
684            newCPU = ourAllocatedCPU;
685        } else {
686            goto exit;
687        }
688    }
689
690    if (!newCPU)
691        goto exit;
692
693    plimitNum = CFDictionaryGetValue(newCPU, CFSTR(kIOPMCPUPowerLimitProcessorSpeedKey));
694    if (plimitNum) {
695        CFNumberGetValue(plimitNum, kCFNumberIntType, &plimit);
696    }
697
698    cpuCountNum = CFDictionaryGetValue(newCPU, CFSTR(kIOPMCPUPowerLimitProcessorCountKey));
699    if (cpuCountNum) {
700        CFNumberGetValue(cpuCountNum, kCFNumberIntType, &cpuCount);
701    }
702
703    runnableTimeNum = CFDictionaryGetValue(newCPU, CFSTR(kIOPMCPUPowerLimitSchedulerTimeKey));
704    if (runnableTimeNum) {
705        CFNumberGetValue(runnableTimeNum, kCFNumberIntType, &runnableTime);
706    }
707
708    // This test only tests the results that are returned.
709    // For a platform that doesn't support, say, dropping CPU's, that property
710    // may be absent in the CPU Power dictionary.
711    if (50 >= plimit) {
712        plimitBelowThreshold = true;
713    }
714    if (maxCPUCount > cpuCount) {
715        coresConstrained = true;
716    }
717    if (100 != runnableTime) {
718        forcedIdle = true;
719    }
720
721    shareTheSystemLoad(kYesNotify);
722
723exit:
724    if (ourAllocatedCPU)
725        CFRelease(ourAllocatedCPU);
726    return;
727}
728
729/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
730
731#if !TARGET_OS_EMBEDDED
732/* @function SystemLoadUserStateHasChanged
733 * Populates:
734 *  userActive.loggedIn
735 *  loggedInUserIdle
736 *  switchedOutUsers
737 *  remoteConnections
738 */
739__private_extern__ void SystemLoadUserStateHasChanged(void)
740{
741    CFStringRef         loggedInUserName;
742
743    userActive.loggedIn = false;
744
745    loggedInUserName = SCDynamicStoreCopyConsoleUser(_getSharedPMDynamicStore(),
746                                                    NULL,  // uid
747                                                    NULL); // gid
748    if (loggedInUserName) {
749        userActive.loggedIn = true;
750        CFRelease(loggedInUserName);
751    }
752
753    shareTheSystemLoad(kYesNotify);
754
755    updateUserPresentActive( );
756}
757
758__private_extern__ void SystemLoadSystemPowerStateHasChanged(void)
759{
760    shareTheSystemLoad(kYesNotify);
761}
762#endif /* !TARGET_OS_EMBEDDED */
763
764/*! SystemLoadUserActiveAssertions
765 *  This timer fires 5 minutes after UserActive assertion was created,
766 *  e.g. after last user input.
767 */
768__private_extern__ void SystemLoadUserActiveAssertions(bool _userActiveAssertions)
769{
770#if !TARGET_OS_EMBEDDED
771    static uint64_t    userActive_ts = 0;
772
773    if (userActive.timer == 0) {
774        userActive.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0,
775                            0, dispatch_get_main_queue());
776
777        dispatch_source_set_event_handler(userActive.timer, ^{
778                SystemLoadUserActiveAssertions(false);
779                });
780
781        dispatch_source_set_cancel_handler(userActive.timer, ^{
782            dispatch_release(userActive.timer);
783            userActive.timer = 0;
784        });
785    }
786
787    /* Determine whether 5 minutes have elapsed since
788     * UserActive assertion was created.
789     */
790    if (_userActiveAssertions == false) {
791        uint64_t curTime = getMonotonicTime();
792        dispatch_suspend(userActive.timer);
793        if ( (curTime - userActive_ts) < IDLE_HID_ACTIVITY_SECS) {
794            dispatch_source_set_timer(userActive.timer,
795                    dispatch_time(DISPATCH_TIME_NOW,
796                        (IDLE_HID_ACTIVITY_SECS+userActive_ts-curTime)*NSEC_PER_SEC),
797                    DISPATCH_TIME_FOREVER, 0);
798            dispatch_resume(userActive.timer);
799            return;
800        }
801    }
802    else {
803        userActive_ts = getMonotonicTime();
804    }
805
806    if (userActive.assertionsActive == _userActiveAssertions)
807       return;
808
809    /* UserActive assertion either just raised, or just released.
810     * We'll update the User PresentActive level accordingly.
811     */
812
813    userActive.assertionsActive = _userActiveAssertions;
814
815    updateUserPresentActive();
816
817
818    /* TODO: Can we remove this timer polling, since we're getting a timeout
819     * above?
820     */
821    if (userActive.assertionsActive == true) {
822        dispatch_source_set_timer(userActive.timer,
823                dispatch_time(DISPATCH_TIME_NOW, IDLE_HID_ACTIVITY_SECS * NSEC_PER_SEC),
824                DISPATCH_TIME_FOREVER, 0);
825        dispatch_resume(userActive.timer);
826    }
827
828#endif /* !TARGET_OS_EMBEDDED */
829}
830
831
832static int minOfThree(int a, int b, int c)
833{
834    int result = a;
835
836    if (b < result) result = b;
837        if (c < result) result = c;
838
839        return result;
840}
841