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 ¬ification_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