VirtualBox

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

Last change on this file since 33451 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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