VirtualBox

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

Last change on this file since 98073 was 96407, checked in by vboxsync, 3 years ago

scm copyright and license note update

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

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