VirtualBox

source: vbox/trunk/src/VBox/Main/darwin/HostPowerDarwin.cpp@ 16596

Last change on this file since 16596 was 15905, checked in by vboxsync, 16 years ago

Main-OSX: Trigger save state on power low event.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.8 KB
Line 
1/** @file
2 *
3 * VirtualBox interface to host's power notification service
4 */
5
6/*
7 * Copyright (C) 2008 Sun Microsystems, Inc.
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
18 * Clara, CA 95054 USA or visit http://www.sun.com if you need
19 * additional information or have any questions.
20 */
21
22#include "HostPower.h"
23#include "Logging.h"
24
25#include <IOKit/IOMessage.h>
26#include <IOKit/ps/IOPowerSources.h>
27#include <IOKit/ps/IOPSKeys.h>
28
29#define POWER_SOURCE_OUTLET 1
30#define POWER_SOURCE_BATTERY 2
31
32HostPowerServiceDarwin::HostPowerServiceDarwin (VirtualBox *aVirtualBox)
33 : HostPowerService (aVirtualBox)
34 , mThread (NULL)
35 , mRootPort (MACH_PORT_NULL)
36 , mNotifyPort (nil)
37 , mRunLoop (nil)
38 , mCritical (false)
39{
40 /* Create the new worker thread. */
41 int rc = RTThreadCreate (&mThread, HostPowerServiceDarwin::powerChangeNotificationThread, this, 65536,
42 RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "MainPower");
43
44 if (RT_FAILURE (rc))
45 LogFlow (("RTThreadCreate failed with %Rrc\n", rc));
46}
47
48HostPowerServiceDarwin::~HostPowerServiceDarwin()
49{
50 /* Jump out of the run loop. */
51 CFRunLoopStop (mRunLoop);
52 /* Remove the sleep notification port from the application runloop. */
53 CFRunLoopRemoveSource (CFRunLoopGetCurrent(),
54 IONotificationPortGetRunLoopSource (mNotifyPort),
55 kCFRunLoopCommonModes);
56 /* Deregister for system sleep notifications. */
57 IODeregisterForSystemPower (&mNotifierObject);
58 /* IORegisterForSystemPower implicitly opens the Root Power Domain
59 * IOService so we close it here. */
60 IOServiceClose (mRootPort);
61 /* Destroy the notification port allocated by IORegisterForSystemPower */
62 IONotificationPortDestroy (mNotifyPort);
63}
64
65
66DECLCALLBACK(int) HostPowerServiceDarwin::powerChangeNotificationThread (RTTHREAD ThreadSelf, void *pInstance)
67{
68 HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *> (pInstance);
69
70 /* We have to initial set the critical state of the battery, cause we want
71 * not the HostPowerService to inform about that state when a VM starts.
72 * See lowPowerHandler for more info. */
73 pPowerObj->checkBatteryCriticalLevel();
74
75 /* Register to receive system sleep notifications */
76 pPowerObj->mRootPort = IORegisterForSystemPower (pPowerObj, &pPowerObj->mNotifyPort,
77 HostPowerServiceDarwin::powerChangeNotificationHandler,
78 &pPowerObj->mNotifierObject);
79 if (pPowerObj->mRootPort == MACH_PORT_NULL)
80 {
81 LogFlow (("IORegisterForSystemPower failed\n"));
82 return VERR_NOT_SUPPORTED;
83 }
84 pPowerObj->mRunLoop = CFRunLoopGetCurrent();
85 /* Add the notification port to the application runloop */
86 CFRunLoopAddSource (pPowerObj->mRunLoop,
87 IONotificationPortGetRunLoopSource (pPowerObj->mNotifyPort),
88 kCFRunLoopCommonModes);
89
90 /* Register for all battery change events. The handler will check for low
91 * power events themself. */
92 CFRunLoopSourceRef runLoopSource = IOPSNotificationCreateRunLoopSource(HostPowerServiceDarwin::lowPowerHandler,
93 pPowerObj);
94 CFRunLoopAddSource(pPowerObj->mRunLoop,
95 runLoopSource,
96 kCFRunLoopCommonModes);
97
98 /* Start the run loop. This blocks. */
99 CFRunLoopRun();
100 return VINF_SUCCESS;
101}
102
103void HostPowerServiceDarwin::powerChangeNotificationHandler (void *pvData, io_service_t service, natural_t messageType, void *pMessageArgument)
104{
105 HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *> (pvData);
106 Log (( "powerChangeNotificationHandler: messageType %08lx, arg %08lx\n", (long unsigned int)messageType, (long unsigned int)pMessageArgument));
107
108 switch (messageType)
109 {
110 case kIOMessageCanSystemSleep:
111 {
112 /* Idle sleep is about to kick in. This message will not be
113 * sent for forced sleep. Applications have a chance to prevent
114 * sleep by calling IOCancelPowerChange. Most applications
115 * should not prevent idle sleep. Power Management waits up to
116 * 30 seconds for you to either allow or deny idle sleep. If
117 * you don't acknowledge this power change by calling either
118 * IOAllowPowerChange or IOCancelPowerChange, the system will
119 * wait 30 seconds then go to sleep. */
120 IOAllowPowerChange (pPowerObj->mRootPort, reinterpret_cast<long> (pMessageArgument));
121 break;
122 }
123 case kIOMessageSystemWillSleep:
124 {
125 /* The system will go for sleep. */
126 pPowerObj->notify (HostPowerEvent_Suspend);
127 /* If you do not call IOAllowPowerChange or IOCancelPowerChange to
128 * acknowledge this message, sleep will be delayed by 30 seconds.
129 * NOTE: If you call IOCancelPowerChange to deny sleep it returns
130 * kIOReturnSuccess, however the system WILL still go to sleep. */
131 IOAllowPowerChange (pPowerObj->mRootPort, reinterpret_cast<long> (pMessageArgument));
132 break;
133 }
134 case kIOMessageSystemWillPowerOn:
135 {
136 /* System has started the wake up process. */
137 break;
138 }
139 case kIOMessageSystemHasPoweredOn:
140 {
141 /* System has finished the wake up process. */
142 pPowerObj->notify (HostPowerEvent_Resume);
143 break;
144 }
145 default:
146 break;
147 }
148}
149
150void HostPowerServiceDarwin::lowPowerHandler (void *pvData)
151{
152 HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *> (pvData);
153
154 /* Following role for sending the BatteryLow event (5% is critical):
155 * - Not at VM start even if the battery is in an critical state already.
156 * - When the power cord is removed so the power supply change from AC to
157 * battery & the battery is in an critical state nothing is triggered.
158 * This has to be discussed.
159 * - When the power supply is the battery & the state of the battery level
160 * changed from normal to critical. The state transition from critical to
161 * normal triggers nothing. */
162 bool fCriticalStateChanged = false;
163 pPowerObj->checkBatteryCriticalLevel (&fCriticalStateChanged);
164 if (fCriticalStateChanged)
165 pPowerObj->notify (HostPowerEvent_BatteryLow);
166}
167
168void HostPowerServiceDarwin::checkBatteryCriticalLevel (bool *pfCriticalChanged)
169{
170 CFTypeRef pBlob = IOPSCopyPowerSourcesInfo();
171 CFArrayRef pSources = IOPSCopyPowerSourcesList (pBlob);
172
173 CFDictionaryRef pSource = NULL;
174 const void *psValue;
175 bool result;
176 int powerSource = POWER_SOURCE_OUTLET;
177 bool critical = false;
178
179 if (CFArrayGetCount (pSources) > 0)
180 {
181 for(int i = 0; i < CFArrayGetCount (pSources); ++i)
182 {
183 pSource = IOPSGetPowerSourceDescription (pBlob, CFArrayGetValueAtIndex (pSources, i));
184 /* If the source is empty skip over to the next one. */
185 if(!pSource)
186 continue;
187 /* Skip all power sources which are currently not present like a
188 * second battery. */
189 if (CFDictionaryGetValue (pSource, CFSTR (kIOPSIsPresentKey)) == kCFBooleanFalse)
190 continue;
191 /* Only internal power types are of interest. */
192 result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSTransportTypeKey), &psValue);
193 if (result &&
194 CFStringCompare ((CFStringRef)psValue, CFSTR (kIOPSInternalType), 0) == kCFCompareEqualTo)
195 {
196 /* First check which power source we are connect on. */
197 result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSPowerSourceStateKey), &psValue);
198 if (result &&
199 CFStringCompare ((CFStringRef)psValue, CFSTR (kIOPSACPowerValue), 0) == kCFCompareEqualTo)
200 powerSource = POWER_SOURCE_OUTLET;
201 else if (result &&
202 CFStringCompare ((CFStringRef)psValue, CFSTR (kIOPSBatteryPowerValue), 0) == kCFCompareEqualTo)
203 powerSource = POWER_SOURCE_BATTERY;
204
205 int curCapacity = 0;
206 int maxCapacity = 1;
207 float remCapacity = 0.0f;
208
209 /* Fetch the current capacity value of the power source */
210 result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSCurrentCapacityKey), &psValue);
211 if (result)
212 CFNumberGetValue ((CFNumberRef)psValue, kCFNumberSInt32Type, &curCapacity);
213 /* Fetch the maximum capacity value of the power source */
214 result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSMaxCapacityKey), &psValue);
215 if (result)
216 CFNumberGetValue ((CFNumberRef)psValue, kCFNumberSInt32Type, &maxCapacity);
217
218 /* Calculate the remaining capacity in percent */
219 remCapacity = ((float)curCapacity/(float)maxCapacity * 100.0);
220
221 /* Check for critical. 5 percent is default. */
222 int criticalValue = 5;
223 result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSDeadWarnLevelKey), &psValue);
224 if (result)
225 CFNumberGetValue ((CFNumberRef)psValue, kCFNumberSInt32Type, &criticalValue);
226 critical = (remCapacity < criticalValue);
227 /* We have to take action only if we are on battery, the
228 * previous state wasn't critical, the state has changed & the
229 * user requested that info. */
230 if (powerSource == POWER_SOURCE_BATTERY &&
231 mCritical == false &&
232 mCritical != critical &&
233 pfCriticalChanged)
234 *pfCriticalChanged = true;
235 Log (("checkBatteryCriticalLevel: Remains: %.0f%% Critical: %d Critical State Changed: %d\n", remCapacity, critical, pfCriticalChanged?*pfCriticalChanged:-1));
236 }
237 }
238 }
239 /* Save the new state */
240 mCritical = critical;
241
242 CFRelease (pBlob);
243 CFRelease (pSources);
244}
245
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette