VirtualBox

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

Last change on this file since 90434 was 85259, checked in by vboxsync, 4 years ago

Main/HostPowerDarwin.cpp: float/double constant mix. bugref:9790

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