x74*_*353 5 macos cocoa kernel iokit
编辑: 在做出一些重要的新发现并且这个问题还没有任何答案之后,我对这个问题进行了大量编辑。
从历史上看/AFAIK,在关闭显示模式下保持 Mac 唤醒并且不满足Apple 的要求,只有使用内核扩展(kext) 或以 root 身份运行的命令才有可能。然而,最近我发现必须有另一种方式。我真的可以使用一些帮助来弄清楚如何在(100% 免费,无 IAP)沙盒 Mac App Store (MAS) 兼容应用程序中使用它。
我已经确认其他一些 MAS 应用程序能够执行此操作,并且看起来它们可能正在写入YES
名为clamshellSleepDisabled
. 或者可能还有其他一些技巧导致键值设置为 YES?我在IOPMrootDomain.cpp 中找到了这个函数:
void IOPMrootDomain::setDisableClamShellSleep( bool val )
{
if (gIOPMWorkLoop->inGate() == false) {
gIOPMWorkLoop->runAction(
OSMemberFunctionCast(IOWorkLoop::Action, this, &IOPMrootDomain::setDisableClamShellSleep),
(OSObject *)this,
(void *)val);
return;
}
else {
DLOG("setDisableClamShellSleep(%x)\n", (uint32_t) val);
if ( clamshellSleepDisabled != val )
{
clamshellSleepDisabled = val;
// If clamshellSleepDisabled is reset to 0, reevaluate if
// system need to go to sleep due to clamshell state
if ( !clamshellSleepDisabled && clamshellClosed)
handlePowerNotification(kLocalEvalClamshellCommand);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我想尝试一下,看看是否只需要这样,但我真的不知道如何调用这个函数。它当然不是IOPMrootDomain文档的一部分,而且我似乎找不到任何有用的示例代码用于 IOPMrootDomain 文档中的函数,例如setAggressiveness
或setPMAssertionLevel
。根据 Console 的说法,这里有一些关于幕后发生的事情的证据:
通过为另一个项目调整ControlPlane的一些源代码,我有一点使用 IOMProotDomain 的经验,但我不知道如何开始。任何帮助将不胜感激。谢谢!
编辑:通过@pmdj 的贡献/答案,这已经解决了!
完整示例项目:https : //github.com/x74353/CDMManager
这最终出人意料地简单/直截了当:
1. 导入标题:
#import <IOKit/pwr_mgt/IOPMLib.h>
Run Code Online (Sandbox Code Playgroud)
2. 在你的实现文件中添加这个函数:
IOReturn RootDomain_SetDisableClamShellSleep (io_connect_t root_domain_connection, bool disable)
{
uint32_t num_outputs = 0;
uint32_t input_count = 1;
uint64_t input[input_count];
input[0] = (uint64_t) { disable ? 1 : 0 };
return IOConnectCallScalarMethod(root_domain_connection, kPMSetClamshellSleepState, input, input_count, NULL, &num_outputs);
}
Run Code Online (Sandbox Code Playgroud)
3. 使用以下命令从您的实现中的其他地方调用上述函数:
io_connect_t connection = IO_OBJECT_NULL;
io_service_t pmRootDomain = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOPMrootDomain"));
IOServiceOpen (pmRootDomain, current_task(), 0, &connection);
// 'enable' is a bool you should assign a YES or NO value to prior to making this call
RootDomain_SetDisableClamShellSleep(connection, enable);
IOServiceClose(connection);
Run Code Online (Sandbox Code Playgroud)
我对 PM 根域没有个人经验,但我对 IOKit 有丰富的经验,所以这里是:
\n\nIOPMrootDomain::setDisableClamShellSleep()
被召唤。setDisableClamShellSleep()
的位置。这当然是有前途的,就像响应用户空间程序调用函数系列时所调用的那样。RootDomainUserClient::externalMethod()
iokit/Kernel/RootDomainUserClient.cpp
externalMethod()
IOConnectCall*()
让我们深入研究一下:
\n\nIOReturn RootDomainUserClient::externalMethod(\n uint32_t selector,\n IOExternalMethodArguments * arguments,\n IOExternalMethodDispatch * dispatch __unused,\n OSObject * target __unused,\n void * reference __unused )\n{\n IOReturn ret = kIOReturnBadArgument;\n\n switch (selector)\n {\n\xe2\x80\xa6\n\xe2\x80\xa6\n\xe2\x80\xa6\n case kPMSetClamshellSleepState:\n fOwner->setDisableClamShellSleep(arguments->scalarInput[0] ? true : false);\n ret = kIOReturnSuccess;\n break;\n\xe2\x80\xa6\n
Run Code Online (Sandbox Code Playgroud)\n\n因此,要调用setDisableClamShellSleep()
您需要:
IOPMrootDomain
。这看起来很简单,因为:\n\nIOPMrootDomain
具有IOUserClientClass
属性RootDomainUserClient
,因此IOServiceOpen()
默认情况下将从用户空间创建一个RootDomainUserClient
实例。IOPMrootDomain
不会覆盖newUserClient
成员函数,因此那里没有访问控制。RootDomainUserClient::initWithTask()
似乎没有对连接用户空间进程施加任何限制(例如 root 用户、代码签名)。 io_connect_t connection = IO_OBJECT_NULL;\n IOReturn ret = IOServiceOpen(\n root_domain_service,\n current_task(),\n 0, // user client type, ignored\n &connection);\n
Run Code Online (Sandbox Code Playgroud)\n\nkPMSetClamshellSleepState
.arguments->scalarInput[0]
为零时将调用setDisableClamShellSleep(false)
,非零值时将调用setDisableClamShellSleep(true)
。IOReturn RootDomain_SetDisableClamShellSleep(io_connect_t root_domain_connection, bool disable)\n{\n uint32_t num_outputs = 0;\n uint64_t inputs[] = { disable ? 1 : 0 };\n return IOConnectCallScalarMethod(\n root_domain_connection, kPMSetClamshellSleepState,\n &inputs, 1, // 1 = length of array \'inputs\'\n NULL, &num_outputs);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\nio_connect_t
手柄后,不要忘记IOServiceClose()
它。这应该可以让您打开或关闭翻盖睡眠。请注意,似乎没有任何自动将值重置为其原始状态的规定,因此如果您的程序崩溃或退出而没有自行清理,则最后设置的任何状态都将保留。从用户体验的角度来看,这可能不太好,因此也许尝试以某种方式防御它,例如在崩溃处理程序中。
\n 归档时间: |
|
查看次数: |
708 次 |
最近记录: |