【问题标题】:Disconnect or connect an iPhone call programmatically以编程方式断开或连接 iPhone 通话
【发布时间】:2026-01-22 16:40:01
【问题描述】:

我正在为 iOS 进行个人调整。我想在电话显示任何内容之前断开/连接电话。我正在使用SBUIFullscreenAlertAdapter 类的initWithAlertController: 方法。当我只显示一条显示来电号码及其姓名的消息时,一切都很好,但是当我尝试以编程方式接听电话或断开连接时,它会崩溃并进入安全模式。

这是我的代码:

@interface SBUIFullscreenAlertAdapter
- (id)initWithAlertController:(id)arg1;
@end

@interface MPIncomingPhoneCallController
{
    struct __CTCall *_incomingCall;
}
- (id) incomingCallNumber;
- (void)stopRingingOrVibrating;
- (void)answerCall:(struct __CTCall *)arg1;
@end

%hook SBUIFullscreenAlertAdapter
- (id)initWithAlertController:(id)arg1
{
    MPIncomingPhoneCallController *phoneCall = (MPIncomingPhoneCallController*)arg1;
    [phoneCall stopRingingOrVibrating];
    if([phoneCall.incomingCallNumber isEqualToString:@"+98.........."]) {
        [phoneCall answerCall:_incomingCall];
    }
    %orig;
    return self;
}
%end

错误是它说:“使用未声明的标识符'_incomingCall'”。

我该如何解决这个问题?有没有办法在挂钩方法时使用私有实例变量?是否有返回来电的CTCallRef* 的函数?有没有其他方法可以做到这一点?

应该很明显我是在为越狱的iOS设备写代码,所以使用私有框架是没有问题的。

【问题讨论】:

    标签: ios iphone objective-c jailbreak


    【解决方案1】:

    有更好的地方可以做到这一点 - MPTelephonyManager -(void)displayAlertForCall:(id)call。此方法位于IncomingCall.servicebundle 二进制文件中,而不是 SpringBoard 本身。当有来电时,这个二进制文件在运行时被加载到 SpringBoard 中。在此之前IncomingCall.servicebundle 未加载,因此您无法挂钩它的方法。


    挂钩 IncomingCall.servicebundle

    为了钩子方法,首先,阅读这个How to hook methods of MPIncomingPhoneCallController? SBPluginManager is loading *.servicebundle binaries at runtime。你需要挂钩它的-(Class)loadPluginBundle:(id)bundle 方法。它看起来像这样:

    void displayAlertForCall_hooked(id self, SEL _cmd, id arg1);
    void(*displayAlertForCall_orig)(id, SEL, id) = NULL;
    
    %hook SBPluginManager
    -(Class)loadPluginBundle:(NSBundle*)bundle
    {
        Class ret = %orig;
    
        if ([[bundle bundleIdentifier] isEqualToString:@"com.apple.mobilephone.incomingcall"] && [bundle isLoaded])
        {
            MSHookMessageEx(objc_getClass("MPTelephonyManager"),
                            @selector(displayAlertForCall:), 
                            (IMP)displayAlertForCall_hooked, 
                            (IMP*)&displayAlertForCall_orig);
        }
    
        return ret;
    }
    %end
    

    如您所见,挂钩被推迟到 IncomingCall.servicebundle 被加载。我不太了解徽标/theos,但我认为它做不到。这就是我使用 CydiaSubstrate (MobileSubstrate) API 的原因。


    挂钩 MPTelephonyManager

    #define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)
    typedef void* CTCallRef;
    void CTCallDisconnect(CTCallRef);
    void CTCallAnswer(CTCallRef);    
    
    void displayAlertForCall_hooked(id self, SEL _cmd, id arg1)
    {
        CTCallRef call = NULL;
        if (SYSTEM_VERSION_LESS_THAN(@"7.0"))
        {
            //On iOS 6 and below arg1 has CTCallRef type
            call = arg1;
        }
        else
        {
           //On iOS 7 arg1 has TUTelephonyCall* type
           call = [arg1 call];
        }
    
        NSString *callNumber = (NSString*)CFBridgingRelease(CTCallCopyAddress(NULL, call));
        if ([callNumber isEqualToString:@"+98.........."]) 
        {
            CTCallAnswer(call);
            //CTCallDisconnect(call);
        }
    
        %orig;
    }
    

    【讨论】:

    • 非常感谢您的回答。我会测试它并尽快回复!
    • [at]creker 谢谢。有效! MSHookMessageEx 函数有一个错误。它只有 4 个参数,因此不需要末尾的 NULL。顺便问一下,您知道如何禁用电话通话时显示的锁定屏幕中的姓名以及如何禁用呼叫栏吗?
    • 如果您想完全隐藏电话而没有任何迹象表明它处于活动状态,那么是的,我知道该怎么做,我在 iOS 5-7 上做到了。但它比我这里的答案复杂得多。它需要在不同的进程中挂钩许多方法(数十种方法)。在 iOS 6 及更低版本上尤其复杂。在 iOS 7 上,Apple 显着改进了 iOS 电话组件的架构,从而使您可以更轻松地做您想做的事情。
    • 我真的不想发布完整的示例,因为它需要在我的代码中重写很多东西——而且我不能出于不同的原因复制粘贴我的代码。你可以问另一个问题,我可以为你指明正确的方向,但最后你必须自己做,因为这真的取决于你想要实现的目标。
    • @Hamed,看看吧。
    【解决方案2】:

    对于 iOS 8.*:

    Theos/Logos 的挂钩似乎很容易。

    示例 Tweak.xm 文件(您需要 8.1 的 TelephonyUtilities 私有框架标头):

    #import "TelephonyUtilities/TUTelephonyCall.h"
    
    %hook MPTelephonyManager
    
    -(void)displayAlertForCall:(TUTelephonyCall*)phoneCall { // for iOS 9: displayAlertForCallIfNecessary
        NSLog(@"hooked displayAlertForCall method");
        if ([[NSBundle mainBundle].bundleIdentifier isEqualToString:@"com.apple.springboard"]) { // (don't know if required)
            [phoneCall answer]; // or [phoneCall disconnect];
        }
        %orig;
    }
    
    %end
    
    
    %ctor {
        if ([[NSBundle bundleWithPath:@"/System/Library/SpringBoardPlugins/IncomingCall.servicebundle"] load]) {
            NSLog(@"IncomingCall.servicebundle loaded succesfully!");
        }
        else {
            NSLog(@"IncomingCall.servicebundle did not load succesfully.");
        }
    }
    

    感谢 Phillip Tennen (https://github.com/codyd51/CallConnect)

    【讨论】:

    • 这似乎不再适用于 iOS 9.x。我想 displayAlertForCall 的签名已经改变或类似的东西。目前,我无法在任何地方找到 IncomingCall.servicebundle 的 iOS 9 标头,而且我自己也没有运气转储它们。我们在哪里/如何获取 IncomingCall.servicebundle 的 iOS 9 标头?
    • 对于 iOS 9,displayAlertForCall 已更改为 displayAlertForCallIfNecessary(如此处所示github.com/CPDigitalDarkroom/iOS9-SpringBoard-Headers/blob/…
    最近更新 更多