VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp@ 70030

Last change on this file since 70030 was 69500, checked in by vboxsync, 7 years ago

*: scm --update-copyright-year

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