VirtualBox

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

Last change on this file since 102869 was 98288, checked in by vboxsync, 23 months ago

Main/src-server: rc -> hrc/vrc (partial). bugref:10223

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