69

Is it theoretically possible to record a phone call on iPhone?

I'm accepting answers which:

  • may or may not require the phone to be jailbroken
  • may or may not pass apple's guidelines due to use of private API's (I don't care; it is not for the App Store)
  • may or may not use private SDKs

I don't want answers just bluntly saying "Apple does not allow that". I know there would be no official way of doing it, and certainly not for an App Store application, and I know there are call recording apps which place outgoing calls through their own servers.

TylerH
  • 20,799
  • 66
  • 75
  • 101
Adam Dempsey
  • 2,913
  • 5
  • 24
  • 26

5 Answers5

86

Here you go. Complete working example. Tweak should be loaded in mediaserverd daemon. It will record every phone call in /var/mobile/Media/DCIM/result.m4a. Audio file has two channels. Left is microphone, right is speaker. On iPhone 4S call is recorded only when the speaker is turned on. On iPhone 5, 5C and 5S call is recorded either way. There might be small hiccups when switching to/from speaker but recording will continue.

#import <AudioToolbox/AudioToolbox.h>
#import <libkern/OSAtomic.h>

//CoreTelephony.framework
extern "C" CFStringRef const kCTCallStatusChangeNotification;
extern "C" CFStringRef const kCTCallStatus;
extern "C" id CTTelephonyCenterGetDefault();
extern "C" void CTTelephonyCenterAddObserver(id ct, void* observer, CFNotificationCallback callBack, CFStringRef name, void *object, CFNotificationSuspensionBehavior sb);
extern "C" int CTGetCurrentCallCount();
enum
{
    kCTCallStatusActive = 1,
    kCTCallStatusHeld = 2,
    kCTCallStatusOutgoing = 3,
    kCTCallStatusIncoming = 4,
    kCTCallStatusHanged = 5
};

NSString* kMicFilePath = @"/var/mobile/Media/DCIM/mic.caf";
NSString* kSpeakerFilePath = @"/var/mobile/Media/DCIM/speaker.caf";
NSString* kResultFilePath = @"/var/mobile/Media/DCIM/result.m4a";

OSSpinLock phoneCallIsActiveLock = 0;
OSSpinLock speakerLock = 0;
OSSpinLock micLock = 0;

ExtAudioFileRef micFile = NULL;
ExtAudioFileRef speakerFile = NULL;

BOOL phoneCallIsActive = NO;

void Convert()
{
    //File URLs
    CFURLRef micUrl = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)kMicFilePath, kCFURLPOSIXPathStyle, false);
    CFURLRef speakerUrl = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)kSpeakerFilePath, kCFURLPOSIXPathStyle, false);
    CFURLRef mixUrl = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)kResultFilePath, kCFURLPOSIXPathStyle, false);

    ExtAudioFileRef micFile = NULL;
    ExtAudioFileRef speakerFile = NULL;
    ExtAudioFileRef mixFile = NULL;

    //Opening input files (speaker and mic)
    ExtAudioFileOpenURL(micUrl, &micFile);
    ExtAudioFileOpenURL(speakerUrl, &speakerFile);

    //Reading input file audio format (mono LPCM)
    AudioStreamBasicDescription inputFormat, outputFormat;
    UInt32 descSize = sizeof(inputFormat);
    ExtAudioFileGetProperty(micFile, kExtAudioFileProperty_FileDataFormat, &descSize, &inputFormat);
    int sampleSize = inputFormat.mBytesPerFrame;

    //Filling input stream format for output file (stereo LPCM)
    FillOutASBDForLPCM(inputFormat, inputFormat.mSampleRate, 2, inputFormat.mBitsPerChannel, inputFormat.mBitsPerChannel, true, false, false);

    //Filling output file audio format (AAC)
    memset(&outputFormat, 0, sizeof(outputFormat));
    outputFormat.mFormatID = kAudioFormatMPEG4AAC;
    outputFormat.mSampleRate = 8000;
    outputFormat.mFormatFlags = kMPEG4Object_AAC_Main;
    outputFormat.mChannelsPerFrame = 2;

    //Opening output file
    ExtAudioFileCreateWithURL(mixUrl, kAudioFileM4AType, &outputFormat, NULL, kAudioFileFlags_EraseFile, &mixFile);
    ExtAudioFileSetProperty(mixFile, kExtAudioFileProperty_ClientDataFormat, sizeof(inputFormat), &inputFormat);

    //Freeing URLs
    CFRelease(micUrl);
    CFRelease(speakerUrl);
    CFRelease(mixUrl);

    //Setting up audio buffers
    int bufferSizeInSamples = 64 * 1024;

    AudioBufferList micBuffer;
    micBuffer.mNumberBuffers = 1;
    micBuffer.mBuffers[0].mNumberChannels = 1;
    micBuffer.mBuffers[0].mDataByteSize = sampleSize * bufferSizeInSamples;
    micBuffer.mBuffers[0].mData = malloc(micBuffer.mBuffers[0].mDataByteSize);

    AudioBufferList speakerBuffer;
    speakerBuffer.mNumberBuffers = 1;
    speakerBuffer.mBuffers[0].mNumberChannels = 1;
    speakerBuffer.mBuffers[0].mDataByteSize = sampleSize * bufferSizeInSamples;
    speakerBuffer.mBuffers[0].mData = malloc(speakerBuffer.mBuffers[0].mDataByteSize);

    AudioBufferList mixBuffer;
    mixBuffer.mNumberBuffers = 1;
    mixBuffer.mBuffers[0].mNumberChannels = 2;
    mixBuffer.mBuffers[0].mDataByteSize = sampleSize * bufferSizeInSamples * 2;
    mixBuffer.mBuffers[0].mData = malloc(mixBuffer.mBuffers[0].mDataByteSize);

    //Converting
    while (true)
    {
        //Reading data from input files
        UInt32 framesToRead = bufferSizeInSamples;
        ExtAudioFileRead(micFile, &framesToRead, &micBuffer);
        ExtAudioFileRead(speakerFile, &framesToRead, &speakerBuffer);
        if (framesToRead == 0)
        {
            break;
        }

        //Building interleaved stereo buffer - left channel is mic, right - speaker
        for (int i = 0; i < framesToRead; i++)
        {
            memcpy((char*)mixBuffer.mBuffers[0].mData + i * sampleSize * 2, (char*)micBuffer.mBuffers[0].mData + i * sampleSize, sampleSize);
            memcpy((char*)mixBuffer.mBuffers[0].mData + i * sampleSize * 2 + sampleSize, (char*)speakerBuffer.mBuffers[0].mData + i * sampleSize, sampleSize);
        }

        //Writing to output file - LPCM will be converted to AAC
        ExtAudioFileWrite(mixFile, framesToRead, &mixBuffer);
    }

    //Closing files
    ExtAudioFileDispose(micFile);
    ExtAudioFileDispose(speakerFile);
    ExtAudioFileDispose(mixFile);

    //Freeing audio buffers
    free(micBuffer.mBuffers[0].mData);
    free(speakerBuffer.mBuffers[0].mData);
    free(mixBuffer.mBuffers[0].mData);
}

void Cleanup()
{
    [[NSFileManager defaultManager] removeItemAtPath:kMicFilePath error:NULL];
    [[NSFileManager defaultManager] removeItemAtPath:kSpeakerFilePath error:NULL];
}

void CoreTelephonyNotificationCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
    NSDictionary* data = (NSDictionary*)userInfo;

    if ([(NSString*)name isEqualToString:(NSString*)kCTCallStatusChangeNotification])
    {
        int currentCallStatus = [data[(NSString*)kCTCallStatus] integerValue];

        if (currentCallStatus == kCTCallStatusActive)
        {
            OSSpinLockLock(&phoneCallIsActiveLock);
            phoneCallIsActive = YES;
            OSSpinLockUnlock(&phoneCallIsActiveLock);
        }
        else if (currentCallStatus == kCTCallStatusHanged)
        {
            if (CTGetCurrentCallCount() > 0)
            {
                return;
            }

            OSSpinLockLock(&phoneCallIsActiveLock);
            phoneCallIsActive = NO;
            OSSpinLockUnlock(&phoneCallIsActiveLock);

            //Closing mic file
            OSSpinLockLock(&micLock);
            if (micFile != NULL)
            {
                ExtAudioFileDispose(micFile);
            }
            micFile = NULL;
            OSSpinLockUnlock(&micLock);

            //Closing speaker file
            OSSpinLockLock(&speakerLock);
            if (speakerFile != NULL)
            {
                ExtAudioFileDispose(speakerFile);
            }
            speakerFile = NULL;
            OSSpinLockUnlock(&speakerLock);

            Convert();
            Cleanup();
        }
    }
}

OSStatus(*AudioUnitProcess_orig)(AudioUnit unit, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData);
OSStatus AudioUnitProcess_hook(AudioUnit unit, AudioUnitRenderActionFlags *ioActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inNumberFrames, AudioBufferList *ioData)
{
    OSSpinLockLock(&phoneCallIsActiveLock);
    if (phoneCallIsActive == NO)
    {
        OSSpinLockUnlock(&phoneCallIsActiveLock);
        return AudioUnitProcess_orig(unit, ioActionFlags, inTimeStamp, inNumberFrames, ioData);
    }
    OSSpinLockUnlock(&phoneCallIsActiveLock);

    ExtAudioFileRef* currentFile = NULL;
    OSSpinLock* currentLock = NULL;

    AudioComponentDescription unitDescription = {0};
    AudioComponentGetDescription(AudioComponentInstanceGetComponent(unit), &unitDescription);
    //'agcc', 'mbdp' - iPhone 4S, iPhone 5
    //'agc2', 'vrq2' - iPhone 5C, iPhone 5S
    if (unitDescription.componentSubType == 'agcc' || unitDescription.componentSubType == 'agc2')
    {
        currentFile = &micFile;
        currentLock = &micLock;
    }
    else if (unitDescription.componentSubType == 'mbdp' || unitDescription.componentSubType == 'vrq2')
    {
        currentFile = &speakerFile;
        currentLock = &speakerLock;
    }

    if (currentFile != NULL)
    {
        OSSpinLockLock(currentLock);

        //Opening file
        if (*currentFile == NULL)
        {
            //Obtaining input audio format
            AudioStreamBasicDescription desc;
            UInt32 descSize = sizeof(desc);
            AudioUnitGetProperty(unit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &desc, &descSize);

            //Opening audio file
            CFURLRef url = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)((currentFile == &micFile) ? kMicFilePath : kSpeakerFilePath), kCFURLPOSIXPathStyle, false);
            ExtAudioFileRef audioFile = NULL;
            OSStatus result = ExtAudioFileCreateWithURL(url, kAudioFileCAFType, &desc, NULL, kAudioFileFlags_EraseFile, &audioFile);
            if (result != 0)
            {
                *currentFile = NULL;
            }
            else
            {
                *currentFile = audioFile;

                //Writing audio format
                ExtAudioFileSetProperty(*currentFile, kExtAudioFileProperty_ClientDataFormat, sizeof(desc), &desc);
            }
            CFRelease(url);
        }
        else
        {
            //Writing audio buffer
            ExtAudioFileWrite(*currentFile, inNumberFrames, ioData);
        }

        OSSpinLockUnlock(currentLock);
    }

    return AudioUnitProcess_orig(unit, ioActionFlags, inTimeStamp, inNumberFrames, ioData);
}

__attribute__((constructor))
static void initialize()
{
    CTTelephonyCenterAddObserver(CTTelephonyCenterGetDefault(), NULL, CoreTelephonyNotificationCallback, NULL, NULL, CFNotificationSuspensionBehaviorHold);

    MSHookFunction(AudioUnitProcess, AudioUnitProcess_hook, &AudioUnitProcess_orig);
}

A few words about what's going on. AudioUnitProcess function is used for processing audio streams in order to apply some effects, mix, convert etc. We are hooking AudioUnitProcess in order to access phone call's audio streams. While phone call is active these streams are being processed in various ways.

We are listening for CoreTelephony notifications in order to get phone call status changes. When we receive audio samples we need to determine where they come from - microphone or speaker. This is done using componentSubType field in AudioComponentDescription structure. Now, you might think, why don't we store AudioUnit objects so that we don't need to check componentSubType every time. I did that but it will break everything when you switch speaker on/off on iPhone 5 because AudioUnit objects will change, they are recreated. So, now we open audio files (one for microphone and one for speaker) and write samples in them, simple as that. When phone call ends we will receive appropriate CoreTelephony notification and close the files. We have two separate files with audio from microphone and speaker that we need to merge. This is what void Convert() is for. It's pretty simple if you know the API. I don't think I need to explain it, comments are enough.

About locks. There are many threads in mediaserverd. Audio processing and CoreTelephony notifications are on different threads so we need some kind synchronization. I chose spin locks because they are fast and because the chance of lock contention is small in our case. On iPhone 4S and even iPhone 5 all the work in AudioUnitProcess should be done as fast as possible otherwise you will hear hiccups from device speaker which obviously not good.

creker
  • 9,400
  • 1
  • 30
  • 47
  • Excactly what i was looking for! – hfossli Feb 05 '14 at 07:56
  • 1
    Here you go. Complete working example tested on iPhone 4S (iOS 6) and iPhone 5 (iOS 7). Later will be testing it on 5C and 5S. I will post the results. – creker Feb 05 '14 at 12:29
  • I know little of the mediaserverd daemon, but this answer surely fills my knowledge hungry brain. I look forward to reading this more closely in the weekend! – hfossli Feb 05 '14 at 13:36
  • Now works on 5C and 5S. These devices use AudioUnits with different `componentSubType`. – creker Feb 06 '14 at 11:16
  • 1
    I think there is only one thing left - make it compatible with conference calls. They way I handle phone calls notifications now will not work with conference calls. – creker Feb 06 '14 at 11:26
  • 3
    Added conference calls support. – creker Feb 06 '14 at 13:04
  • 8
    @orazran, no, it will work only on a jailbroken device. – creker Feb 12 '14 at 12:20
  • @creker Can you help me run this tweak? When I try to run it I get several weird errors.How can I compile and execute this code? – zzzzz Mar 19 '14 at 11:24
  • @creker I am getting this error 'use of undeclared identifier 'AudioUnitprocess' ' – zzzzz Mar 20 '14 at 06:18
  • @creker Please help me with my question.I have been stuck on it for days http://stackoverflow.com/questions/22525908/use-of-undeclared-identifier-error-while-compiling-code – zzzzz Mar 24 '14 at 07:27
  • hi, could you plz explain why on Iphone 4s it only works on speakerphone mode? thanks – grumpynerd May 19 '14 at 09:41
  • @grumpynerd, I don't know. I hook method that process audio streams with various filters/DSPs. I suspect on 4S speakerphone does not require any processing so my hooked method is not being called at all. I didn't tried to solve it so I cannot help you with that. – creker May 19 '14 at 10:17
  • Oh, sorry, it does not call hooked method in normal mode. Call recording on 4S works only with speaker. – creker May 19 '14 at 10:58
  • cheers, probably not worth investing time on it since iphone4s is pretty old and I am about to grab a new iPhone of whatever it comes out in the next release. – grumpynerd May 19 '14 at 11:00
  • We already are on a pretty low level right now. We are hooking low level C audio processing API that's being called inside mediaserverd daemon which deals with all the sound in iOS from all other processes. Lower than that is the kernel. Indeed it's not worth it. I don't feel like reverse engineering mediaserverd just for that when everything works on newer devices. – creker May 19 '14 at 11:07
  • Can anyone please guide me that how should I create tweak with this code. I am using iOSOpendev with xCode 5.0. – Ahad Khan Jun 17 '14 at 11:28
  • Hi I am trying to manipulate the voice data that comes after this example Please check out my question in here http://stackoverflow.com/questions/29329578/how-can-i-change-the-voice-data-iphone-sends-to-cellular-network – John Bassos Mar 29 '15 at 13:45
  • I also tested on iPhone 4 and it doesn't seem to call the hooked audiounitprocess. It does althought triger CoreTelephonyNotificationCallback – John Bassos Apr 03 '15 at 21:19
  • @creker How do we do this? "Tweak should be loaded in mediaserverd daemon"? Does this happen automatically after make install? Or how? – mylord May 05 '15 at 13:10
  • @creker I used theos/bin/nic.pl to create the tweak, and have a Tweak.xm file. Where do I put this code in relation to that file? E.g., can I just replace Tweak.xm contents with this code? Or? – mylord May 05 '15 at 13:17
  • what frameworks/libraries need to be linked to resolve the linking errors? http://hastebin.com/zojadatiwe.avrasm – mylord May 05 '15 at 18:59
  • @mylord, AudioToolbox, CoreTelephony – creker May 05 '15 at 20:26
  • @creker my recordtweak.plist has this: Filter = { Executables = ("mediaserverd"); Mode = "Any"; }; This isn't working: https://gist.github.com/adaptivedev/9ea5ad4c75ca55d1098d – mylord May 07 '15 at 09:34
  • Anyone looking for a working project, please take a look at [here](http://iosre.com/t/write-a-phone-call-recorder-on-ios-8-step-by-step/1185) – snakeninny May 08 '15 at 06:05
  • Fixed small bug. `kCTCallStatusOutgoing` is sent too early and should not be used to start recording. Because the audio units are no exclusive to calls, they're general audio processing units, they can be configured differently when `kCTCallStatusOutgoing` is received. Namely, sample rate may be different (44100 instead of 16000). Shortly after `kCTCallStatusOutgoing` but before `kCTCallStatusActive` they're beign configured property to handle call audio. That leads to bug when one of the output files will use wrong sample rate. When you play it everything will be sped up. – creker Oct 30 '15 at 20:06
  • @creker, you say it won't work on non-jailbroken iPhone. Could you explain why? (edit: sorry, just sat that this tweak needs to be loaded into a system process. Never mind me...) – user3099609 Nov 11 '15 at 14:00
  • @creker: the code seem out of date. Could you update your answer? – lee Feb 25 '17 at 04:44
  • @lee, if you mean iOS 10, then I didn't test it on it. If anyone does, please report back and I will update the answer. – creker Feb 25 '17 at 23:44
  • @creker: Yes, I try to run code with Xcode 8.2.1(iOS 10) but got some problem on it. If can, could you please make an update for your code? – lee Feb 26 '17 at 04:02
  • @creker is this solution accepted by Apple for submission, and if it is not will it work for non-jailbroken phones? – Christopher Nassar Apr 06 '17 at 08:55
  • Hi i am getting errors as "Implicit dec of function 'FillOutASBDForLPCM' is invalid in C99" & "Implicit dec of function 'MSHookFunction' is invalid in C99". please help i am stuck, also tried declaring the same like "extern void MSHookFunction(void *symbol, void *hook, void **old); for both method" but no success. – Master_Tushar Jul 11 '17 at 09:24
  • is there still no way "formal" way of recording incoming/outgoing calls natively in 2023? – oldboy Apr 17 '23 at 09:04
9

Yes. Audio Recorder by a developer named Limneos does that (and quite well). You can find it on Cydia. It can record any type of call on iPhone 5 and up without using any servers etc'. The call will be placed on the device in an Audio file. It also supports iPhone 4S but for speaker only.

This tweak is known to be the first tweak ever that managed to record both streams of audio without using any 3rd party severs, VOIP or something similar.

The developer placed beeps on the other side of the call to alert the person you are recording but those were removed too by hackers across the net. To answer your question, Yes, it's very much possible, and not just theoretically.

enter image description here

Further reading

Community
  • 1
  • 1
Segev
  • 19,035
  • 12
  • 80
  • 152
  • 1
    Great answer. Though, this is already outdated if it doesn't support iOS 7 and iphone 5s. – hfossli Feb 03 '14 at 18:23
  • 3
    @hfossli Who said it doesn't support iOS 7 and iPhone 5s? Works fine here on iPhone 5 with iOS 7 + it supports iPhone 5S. :) – Segev Feb 03 '14 at 18:47
  • @hfossli No that I know of. – Segev Feb 04 '14 at 12:11
  • But how does he manage to do this? I am hungry for knowledge. – hfossli Feb 04 '14 at 12:15
  • @hfossli I'm guessing that A lot of trial & error, a lot of fiddling with Apples private api and possibly some low level c to glue everything together. https://github.com/nst/iOS-Runtime-Headers – Segev Feb 04 '14 at 12:22
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/46771/discussion-between-hfossli-and-segev) – hfossli Feb 04 '14 at 16:38
  • @hfossli Were you able to run the code(The one you awarded the bounty to).If so can you point me in the right direction on how can I compile and execute the code? – zzzzz Mar 19 '14 at 11:29
  • @Worker why are you commenting on this post? – hfossli Mar 19 '14 at 14:53
  • is there still no way "formal" way of recording incoming/outgoing calls natively in 2023? – oldboy Apr 17 '23 at 09:04
7

The only solution I can think of is to use the Core Telephony framework, and more specifically the callEventHandler property, to intercept when a call is coming in, and then to use an AVAudioRecorder to record the voice of the person with the phone (and maybe a little of the person on the other line's voice). This is obviously not perfect, and would only work if your application is in the foreground at the time of the call, but it may be the best you can get. See more about finding out if there is an incoming phone call here: Can we fire an event when ever there is Incoming and Outgoing call in iphone?.

EDIT:

.h:

#import <AVFoundation/AVFoundation.h>
#import<CoreTelephony/CTCallCenter.h>
#import<CoreTelephony/CTCall.h>
@property (strong, nonatomic) AVAudioRecorder *audioRecorder;

ViewDidLoad:

NSArray *dirPaths;
NSString *docsDir;

dirPaths = NSSearchPathForDirectoriesInDomains(
    NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = dirPaths[0];

NSString *soundFilePath = [docsDir
   stringByAppendingPathComponent:@"sound.caf"];

NSURL *soundFileURL = [NSURL fileURLWithPath:soundFilePath];

NSDictionary *recordSettings = [NSDictionary
        dictionaryWithObjectsAndKeys:
        [NSNumber numberWithInt:AVAudioQualityMin],
        AVEncoderAudioQualityKey,
        [NSNumber numberWithInt:16],
        AVEncoderBitRateKey,
        [NSNumber numberWithInt: 2],
        AVNumberOfChannelsKey,
        [NSNumber numberWithFloat:44100.0],
        AVSampleRateKey,
        nil];

NSError *error = nil;

_audioRecorder = [[AVAudioRecorder alloc]
              initWithURL:soundFileURL
              settings:recordSettings
              error:&error];

 if (error)
 {
       NSLog(@"error: %@", [error localizedDescription]);
 } else {
       [_audioRecorder prepareToRecord];
 }

CTCallCenter *callCenter = [[CTCallCenter alloc] init];

[callCenter setCallEventHandler:^(CTCall *call) {
  if ([[call callState] isEqual:CTCallStateConnected]) {
    [_audioRecorder record];
  } else if ([[call callState] isEqual:CTCallStateDisconnected]) {
    [_audioRecorder stop];
  }
}];

AppDelegate.m:

- (void)applicationDidEnterBackground:(UIApplication *)application//Makes sure that the recording keeps happening even when app is in the background, though only can go for 10 minutes.
{
    __block UIBackgroundTaskIdentifier task = 0;
    task=[application beginBackgroundTaskWithExpirationHandler:^{
    NSLog(@"Expiration handler called %f",[application backgroundTimeRemaining]);
    [application endBackgroundTask:task];
    task=UIBackgroundTaskInvalid;
}];

This is the first time using many of these features, so not sure if this is exactly right, but I think you get the idea. Untested, as I do not have access to the right tools at the moment. Compiled using these sources:

Community
  • 1
  • 1
John Farkerson
  • 2,543
  • 2
  • 23
  • 33
3

Apple does not allow it and does not provide any API for it.

However, on a jailbroken device I'm sure it's possible. As a matter of fact, I think it's already done. I remember seeing an app when my phone was jailbroken that changed your voice and recorded the call - I remember it was a US company offering it only in the states. Unfortunately I don't remember the name...

Dimitris
  • 13,480
  • 17
  • 74
  • 94
  • Yea I think that sort of App only works with outgoing calls as they route the calls through their servers, logging it as it goes through. There are a few similar apps available in Cydia. – Adam Dempsey Nov 27 '09 at 15:37
  • Yes, I was talking about SpoofApp, as TechZen noted. – Dimitris Nov 27 '09 at 15:39
  • Yes, SpoofApp probably records on their servers because it's on the AppStore and it records the calls. Anyway, I'm sure you can record calls on a jailbroken iPhone. It's a computer. You can do anything you want when you have unrestricted access (and the required skills). – Dimitris Nov 27 '09 at 15:42
  • No necessarily. It could be that the phone part of the iPhone bypasses the software. – Georg Schölly Nov 27 '09 at 16:10
  • No software at all? Could be. Even in that case, since you have access to the microphone you could be polling data from it and recording it... Anyway, these are wild guesses here. Someone with experience on jailbroken devices could probably enlighten us more. – Dimitris Nov 27 '09 at 16:39
  • @Dimitris Check out my answer. There's an app on Cydia that does it quite well. – Segev Feb 04 '14 at 08:40
2

I guess some hardware could solve this. Connected to the minijack-port; having earbuds and a microphone passing through a small recorder. This recorder can be very simple. While not in conversation the recorder could feed the phone with data/the recording (through the jack-cable). And with a simple start button (just like the volum controls on the earbuds) could be sufficient for timing the recording.

Some setups

hfossli
  • 22,616
  • 10
  • 116
  • 130