I\'m working on a personal tweak for iOS. I want to disconnect/connect a phone call before the phone would show anything. I\'m hooking into the initWithAlertController
For iOS 8.*:
The hooking seems pretty easy with Theos/Logos.
Example Tweak.xm file (you need the TelephonyUtilities private framework headers for 8.1):
#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.");
}
}
Credit to Phillip Tennen (https://github.com/codyd51/CallConnect)
There is much better place to do that - MPTelephonyManager -(void)displayAlertForCall:(id)call
. This method is located in IncomingCall.servicebundle
binary, not in SpringBoard itself. This binary is being loaded at runtime into SpringBoard when there is an incoming call. Before that IncomingCall.servicebundle
is not loaded thus you can't hook it's methods.
In order to hook the method, first, read this How to hook methods of MPIncomingPhoneCallController? SBPluginManager
is loading *.servicebundle binaries at runtime. You need to hook it's -(Class)loadPluginBundle:(id)bundle
method. It's gonna look something like this:
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
As you can see, hooking is deferred until IncomingCall.servicebundle
is loaded. I don't know logos/theos that well but I think it can't do that. That's why I used CydiaSubstrate (MobileSubstrate) API.
#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;
}