+ bool eval_clamshell = false;
+ bool eval_clamshell_alarm = false;
+
+ ASSERT_GATED();
+
+ /*
+ * Local (IOPMrootDomain only) eval clamshell command
+ */
+ if (msg & kLocalEvalClamshellCommand) {
+ if (isRTCAlarmWake) {
+ eval_clamshell_alarm = true;
+
+ // reset isRTCAlarmWake. This evaluation should happen only once
+ // on RTC/Alarm wake. Any clamshell events after wake should follow
+ // the regular evaluation
+ isRTCAlarmWake = false;
+ } else {
+ eval_clamshell = true;
+ }
+ }
+
+ /*
+ * Overtemp
+ */
+ if (msg & kIOPMOverTemp) {
+ MSG("PowerManagement emergency overtemp signal. Going to sleep!");
+ privateSleepSystem(kIOPMSleepReasonThermalEmergency);
+ }
+
+ /*
+ * Forward DW thermal notification to client, if system is not going to sleep
+ */
+ if ((msg & kIOPMDWOverTemp) && (_systemTransitionType != kSystemTransitionSleep)) {
+ DLOG("DarkWake thermal limits message received!\n");
+
+ messageClients(kIOPMMessageDarkWakeThermalEmergency);
+ }
+
+ /*
+ * Sleep Now!
+ */
+ if (msg & kIOPMSleepNow) {
+ privateSleepSystem(kIOPMSleepReasonSoftware);
+ }
+
+ /*
+ * Power Emergency
+ */
+ if (msg & kIOPMPowerEmergency) {
+ lowBatteryCondition = true;
+ privateSleepSystem(kIOPMSleepReasonLowPower);
+ }
+
+ /*
+ * Clamshell OPEN
+ */
+ if (msg & kIOPMClamshellOpened) {
+ DLOG("Clamshell opened\n");
+ // Received clamshel open message from clamshell controlling driver
+ // Update our internal state and tell general interest clients
+ clamshellClosed = false;
+ clamshellExists = true;
+
+ // Don't issue a hid tickle when lid is open and polled on wake
+ if (msg & kIOPMSetValue) {
+ setProperty(kIOPMRootDomainWakeTypeKey, "Lid Open");
+ reportUserInput();
+ }
+
+ // Tell PMCPU
+ informCPUStateChange(kInformLid, 0);
+
+ // Tell general interest clients
+ sendClientClamshellNotification();
+
+ bool aborting = ((lastSleepReason == kIOPMSleepReasonClamshell)
+ || (lastSleepReason == kIOPMSleepReasonIdle)
+ || (lastSleepReason == kIOPMSleepReasonMaintenance));
+ if (aborting) {
+ userActivityCount++;
+ }
+ DLOG("clamshell tickled %d lastSleepReason %d\n", userActivityCount, lastSleepReason);
+ }
+
+ /*
+ * Clamshell CLOSED
+ * Send the clamshell interest notification since the lid is closing.
+ */
+ if (msg & kIOPMClamshellClosed) {
+ if (clamshellClosed && clamshellExists) {
+ DLOG("Ignoring redundant Clamshell close event\n");
+ } else {
+ DLOG("Clamshell closed\n");
+ // Received clamshel open message from clamshell controlling driver
+ // Update our internal state and tell general interest clients
+ clamshellClosed = true;
+ clamshellExists = true;
+
+ // Tell PMCPU
+ informCPUStateChange(kInformLid, 1);
+
+ // Tell general interest clients
+ sendClientClamshellNotification();
+
+ // And set eval_clamshell = so we can attempt
+ eval_clamshell = true;
+ }
+ }
+
+ /*
+ * Set Desktop mode (sent from graphics)
+ *
+ * -> reevaluate lid state
+ */
+ if (msg & kIOPMSetDesktopMode) {
+ DLOG("Desktop mode\n");
+ desktopMode = (0 != (msg & kIOPMSetValue));
+ msg &= ~(kIOPMSetDesktopMode | kIOPMSetValue);
+
+ sendClientClamshellNotification();
+
+ // Re-evaluate the lid state
+ eval_clamshell = true;
+ }
+
+ /*
+ * AC Adaptor connected
+ *
+ * -> reevaluate lid state
+ */
+ if (msg & kIOPMSetACAdaptorConnected) {
+ acAdaptorConnected = (0 != (msg & kIOPMSetValue));
+ msg &= ~(kIOPMSetACAdaptorConnected | kIOPMSetValue);
+
+ // Tell CPU PM
+ informCPUStateChange(kInformAC, !acAdaptorConnected);
+
+ // Tell BSD if AC is connected
+ // 0 == external power source; 1 == on battery
+ post_sys_powersource(acAdaptorConnected ? 0:1);
+
+ sendClientClamshellNotification();
+
+ // Re-evaluate the lid state
+ eval_clamshell = true;
+
+ // Lack of AC may have latched a display wrangler tickle.
+ // This mirrors the hardware's USB wake event latch, where a latched
+ // USB wake event followed by an AC attach will trigger a full wake.
+ latchDisplayWranglerTickle( false );
+
+#if HIBERNATION
+ // AC presence will reset the standy timer delay adjustment.
+ _standbyTimerResetSeconds = 0;
+#endif
+ if (!userIsActive) {
+ // Reset userActivityTime when power supply is changed(rdr 13789330)
+ clock_get_uptime(&userActivityTime);
+ }
+ }
+
+ /*
+ * Enable Clamshell (external display disappear)
+ *
+ * -> reevaluate lid state
+ */
+ if (msg & kIOPMEnableClamshell) {
+ DLOG("Clamshell enabled\n");
+ // Re-evaluate the lid state
+ // System should sleep on external display disappearance
+ // in lid closed operation.
+ if (true == clamshellDisabled) {
+ eval_clamshell = true;
+ }
+
+ clamshellDisabled = false;
+ sendClientClamshellNotification();
+ }
+
+ /*
+ * Disable Clamshell (external display appeared)
+ * We don't bother re-evaluating clamshell state. If the system is awake,
+ * the lid is probably open.
+ */
+ if (msg & kIOPMDisableClamshell) {
+ DLOG("Clamshell disabled\n");
+ clamshellDisabled = true;
+ sendClientClamshellNotification();
+ }
+
+ /*
+ * Evaluate clamshell and SLEEP if appropiate
+ */
+ if (eval_clamshell_alarm && clamshellClosed) {
+ if (shouldSleepOnRTCAlarmWake()) {
+ privateSleepSystem(kIOPMSleepReasonClamshell);
+ }
+ } else if (eval_clamshell && clamshellClosed) {
+ if (shouldSleepOnClamshellClosed()) {
+ privateSleepSystem(kIOPMSleepReasonClamshell);
+ } else {
+ evaluatePolicy( kStimulusDarkWakeEvaluate );
+ }
+ }
+
+ if (msg & kIOPMProModeEngaged) {
+ int newState = 1;
+ DLOG("ProModeEngaged\n");
+ messageClient(kIOPMMessageProModeStateChange, systemCapabilityNotifier, &newState, sizeof(newState));
+ }
+
+ if (msg & kIOPMProModeDisengaged) {
+ int newState = 0;
+ DLOG("ProModeDisengaged\n");
+ messageClient(kIOPMMessageProModeStateChange, systemCapabilityNotifier, &newState, sizeof(newState));
+ }
+}
+
+//******************************************************************************
+// evaluatePolicy
+//
+// Evaluate root-domain policy in response to external changes.
+//******************************************************************************
+
+void
+IOPMrootDomain::evaluatePolicy( int stimulus, uint32_t arg )
+{
+ union {
+ struct {
+ int idleSleepEnabled : 1;
+ int idleSleepDisabled : 1;
+ int displaySleep : 1;
+ int sleepDelayChanged : 1;
+ int evaluateDarkWake : 1;
+ int adjustPowerState : 1;
+ int userBecameInactive : 1;
+ } bit;
+ uint32_t u32;
+ } flags;
+
+
+ ASSERT_GATED();
+ flags.u32 = 0;
+
+ switch (stimulus) {
+ case kStimulusDisplayWranglerSleep:
+ DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ if (!wranglerAsleep) {
+ // first transition to wrangler sleep or lower
+ flags.bit.displaySleep = true;
+ }
+ break;
+
+ case kStimulusDisplayWranglerWake:
+ DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ displayIdleForDemandSleep = false;
+ wranglerAsleep = false;
+ break;
+
+ case kStimulusEnterUserActiveState:
+ DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ if (_preventUserActive) {
+ DLOG("user active dropped\n");
+ break;
+ }
+ if (!userIsActive) {
+ userIsActive = true;
+ userWasActive = true;
+ clock_get_uptime(&gUserActiveAbsTime);
+
+ // Stay awake after dropping demand for display power on
+ if (kFullWakeReasonDisplayOn == fullWakeReason) {
+ fullWakeReason = fFullWakeReasonDisplayOnAndLocalUser;
+ DLOG("User activity while in notification wake\n");
+ changePowerStateWithOverrideTo( getRUN_STATE(), 0);
+ }
+
+ kdebugTrace(kPMLogUserActiveState, 0, 1, 0);
+ setProperty(gIOPMUserIsActiveKey, kOSBooleanTrue);
+ messageClients(kIOPMMessageUserIsActiveChanged);
+ }
+ flags.bit.idleSleepDisabled = true;
+ break;
+
+ case kStimulusLeaveUserActiveState:
+ DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ if (userIsActive) {
+ clock_get_uptime(&gUserInactiveAbsTime);
+ userIsActive = false;
+ clock_get_uptime(&userBecameInactiveTime);
+ flags.bit.userBecameInactive = true;
+
+ kdebugTrace(kPMLogUserActiveState, 0, 0, 0);
+ setProperty(gIOPMUserIsActiveKey, kOSBooleanFalse);
+ messageClients(kIOPMMessageUserIsActiveChanged);
+ }
+ break;
+
+ case kStimulusAggressivenessChanged:
+ {
+ DMSG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ unsigned long minutesToIdleSleep = 0;
+ unsigned long minutesToDisplayDim = 0;
+ unsigned long minutesDelta = 0;
+
+ // Fetch latest display and system sleep slider values.
+ getAggressiveness(kPMMinutesToSleep, &minutesToIdleSleep);
+ getAggressiveness(kPMMinutesToDim, &minutesToDisplayDim);
+ DLOG("aggressiveness changed: system %u->%u, display %u\n",
+ (uint32_t) sleepSlider,
+ (uint32_t) minutesToIdleSleep,
+ (uint32_t) minutesToDisplayDim);
+
+ DLOG("idle time -> %ld secs (ena %d)\n",
+ idleSeconds, (minutesToIdleSleep != 0));
+
+
+ // How long to wait before sleeping the system once
+ // the displays turns off is indicated by 'extraSleepDelay'.
+
+ if (minutesToIdleSleep > minutesToDisplayDim) {
+ minutesDelta = minutesToIdleSleep - minutesToDisplayDim;
+ } else if (minutesToIdleSleep == minutesToDisplayDim) {
+ minutesDelta = 1;
+ }
+
+ if ((!idleSleepEnabled) && (minutesToIdleSleep != 0)) {
+ idleSleepEnabled = flags.bit.idleSleepEnabled = true;
+ }
+
+ if ((idleSleepEnabled) && (minutesToIdleSleep == 0)) {
+ flags.bit.idleSleepDisabled = true;
+ idleSleepEnabled = false;
+ }
+ if (0x7fffffff == minutesToIdleSleep) {
+ minutesToIdleSleep = idleSeconds;
+ }
+
+ if (((minutesDelta != extraSleepDelay) ||
+ (userActivityTime != userActivityTime_prev)) &&
+ !flags.bit.idleSleepEnabled && !flags.bit.idleSleepDisabled) {
+ flags.bit.sleepDelayChanged = true;
+ }
+
+ if (systemDarkWake && !darkWakeToSleepASAP &&
+ (flags.bit.idleSleepEnabled || flags.bit.idleSleepDisabled)) {
+ // Reconsider decision to remain in dark wake
+ flags.bit.evaluateDarkWake = true;
+ }
+
+ sleepSlider = minutesToIdleSleep;
+ extraSleepDelay = minutesDelta;
+ userActivityTime_prev = userActivityTime;
+ } break;
+
+ case kStimulusDemandSystemSleep:
+ DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ displayIdleForDemandSleep = true;
+ if (wrangler && wranglerIdleSettings) {
+ // Request wrangler idle only when demand sleep is triggered
+ // from full wake.
+ if (CAP_CURRENT(kIOPMSystemCapabilityGraphics)) {
+ wrangler->setProperties(wranglerIdleSettings);
+ DLOG("Requested wrangler idle\n");
+ }
+ }
+ // arg = sleepReason
+ changePowerStateWithOverrideTo( SLEEP_STATE, arg );
+ break;
+
+ case kStimulusAllowSystemSleepChanged:
+ DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ flags.bit.adjustPowerState = true;
+ break;
+
+ case kStimulusDarkWakeActivityTickle:
+ DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ // arg == true implies real and not self generated wrangler tickle.
+ // Update wake type on PM work loop instead of the tickle thread to
+ // eliminate the possibility of an early tickle clobbering the wake
+ // type set by the platform driver.
+ if (arg == true) {
+ setProperty(kIOPMRootDomainWakeTypeKey, kIOPMRootDomainWakeTypeHIDActivity);
+ }
+
+ if (false == wranglerTickled) {
+ if (latchDisplayWranglerTickle(true)) {
+ DLOG("latched tickle\n");
+ break;
+ }
+
+ wranglerTickled = true;
+ DLOG("Requesting full wake after dark wake activity tickle\n");
+ requestFullWake( kFullWakeReasonLocalUser );
+ }
+ break;
+
+ case kStimulusDarkWakeEntry:
+ case kStimulusDarkWakeReentry:
+ DLOG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ // Any system transitions since the last dark wake transition
+ // will invalid the stimulus.
+
+ if (arg == _systemStateGeneration) {
+ DLOG("dark wake entry\n");
+ systemDarkWake = true;
+
+ // Keep wranglerAsleep an invariant when wrangler is absent
+ if (wrangler) {
+ wranglerAsleep = true;
+ }
+
+ if (kStimulusDarkWakeEntry == stimulus) {
+ clock_get_uptime(&userBecameInactiveTime);
+ flags.bit.evaluateDarkWake = true;
+ if (activitySinceSleep()) {
+ DLOG("User activity recorded while going to darkwake\n");
+ reportUserInput();
+ }
+ }
+
+ // Always accelerate disk spindown while in dark wake,
+ // even if system does not support/allow sleep.
+
+ cancelIdleSleepTimer();
+ setQuickSpinDownTimeout();
+ }
+ break;
+
+ case kStimulusDarkWakeEvaluate:
+ DMSG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ if (systemDarkWake) {
+ flags.bit.evaluateDarkWake = true;
+ }
+ break;
+
+ case kStimulusNoIdleSleepPreventers:
+ DMSG("evaluatePolicy( %d, 0x%x )\n", stimulus, arg);
+ flags.bit.adjustPowerState = true;
+ break;
+ } /* switch(stimulus) */
+
+ if (flags.bit.evaluateDarkWake && (kFullWakeReasonNone == fullWakeReason)) {
+ if (darkWakeToSleepASAP ||
+ (clamshellClosed && !(desktopMode && acAdaptorConnected))) {
+ uint32_t newSleepReason;
+
+ if (CAP_HIGHEST(kIOPMSystemCapabilityGraphics)) {
+ // System was previously in full wake. Sleep reason from
+ // full to dark already recorded in fullToDarkReason.
+
+ if (lowBatteryCondition) {
+ newSleepReason = kIOPMSleepReasonLowPower;
+ } else {
+ newSleepReason = fullToDarkReason;
+ }
+ } else {
+ // In dark wake from system sleep.
+
+ if (darkWakeSleepService) {
+ newSleepReason = kIOPMSleepReasonSleepServiceExit;
+ } else {
+ newSleepReason = kIOPMSleepReasonMaintenance;
+ }
+ }
+
+ if (checkSystemCanSleep(newSleepReason)) {
+ privateSleepSystem(newSleepReason);
+ }
+ } else { // non-maintenance (network) dark wake
+ if (checkSystemCanSleep(kIOPMSleepReasonIdle)) {
+ // Release power clamp, and wait for children idle.
+ adjustPowerState(true);
+ } else {
+ changePowerStateToPriv(getRUN_STATE());
+ }
+ }
+ }