Accessing iPhone Call History

Although there is no API in the SDK to access iPhone’s Call History, there is a read-only SQLite database that gives access to the call history. In this tutorial, we shall learn what is the name of this database; where is it stored; what is its schema; and finally how can we access it?

Call History Database – Path, and Schema

The iPhone call history is stored in “call_history.db” which is located at the following path:

/private/var/root/Library/CallHistory/call_history.db

There are other important databases on the iPhone, some of which are accessible and some are not. We can find that out using the following piece of code. This basically enumerates the system folder and subfolders and finds databases that are readable:

NSFileManager *fileManager = [NSFileManager defaultManager]; 
NSDirectoryEnumerator *dirnum = [[NSFileManager defaultManager] enumeratorAtPath: @"/private/"]; 
NSString *nextItem = [NSString string]; 
while( (nextItem = [dirnum nextObject])) {
    if ([[nextItem pathExtension] isEqualToString: @"db"] || 
        [[nextItem pathExtension] isEqualToString: @"sqlitedb"]) { 
        if ([fileManager isReadableFileAtPath:nextItem]) { 
            NSLog(@"%@", nextItem); 
        } 
    } 
}

I got the following results:

var/db/launchd.db
var/mobile/Library/AddressBook/AddressBook.sqlitedb
var/mobile/Library/AddressBook/AddressBookImages.sqlitedb
var/wireless/Library/CallHistory/call_history.db

Feel free to share yours :)

So now we have the database path and we can easily read the values stored in it, but to know what value is what we need to know the schema. Here is a snippet to find that out:

NSString *callHisoryDatabasePath = @"/private/var/wireless/Library/CallHistory/call_history.db";
BOOL callHistoryFileExist = FALSE;
callHistoryFileExist = [fileManager fileExistsAtPath:callHisoryDatabasePath];
[fileManager release];

if(callHistoryFileExist) {
    if ([fileManager isReadableFileAtPath:callHisoryDatabasePath]) {
        sqlite3 *database;
        if(sqlite3_open([callHisoryDatabasePath UTF8String], &database) == SQLITE_OK) {

            //Getting table names and schema
            sqlite3_stmt *compiledStatement;
            NSString *sqlStatement = [NSString stringWithString:
                                    @"SELECT * FROM sqlite_master WHERE type='table';"];
            int errorCode = sqlite3_prepare_v2(database, [sqlStatement UTF8String], -1,
                                                 &compiledStatement, NULL);

            if( errorCode == SQLITE_OK) {
                int count = 1;
                while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
                    // Read the data from the result row
                    int numberOfColumns = sqlite3_column_count(compiledStatement);
                    NSString *data;
                    NSString *columnName;
                    for (int i = 0; i < numberOfColumns; i++) {
                        columnName = [[NSString alloc] initWithUTF8String:
                                     (char *)sqlite3_column_name(compiledStatement, i)];
                        data = [[NSString alloc] initWithUTF8String:
                                 (char *)sqlite3_column_text(compiledStatement, i)];

                        NSLog(@"%@ : %@", columnName, data);

                        [columnName release];
                        [data release];
                    }
                    count++;
                }
            }
            else {
                NSLog(@"Failed to retrieve table");
                NSLog(@"Error Code: %d", errorCode);
            }
            sqlite3_finalize(compiledStatement);
        }
    }
}

You will get something like this:

type : table
 name : _SqliteDatabaseProperties
 tbl_name : _SqliteDatabaseProperties
 rootpage : 2
 sql : CREATE TABLE _SqliteDatabaseProperties (key TEXT, value TEXT, UNIQUE(key))

 type : table
 name : call
 tbl_name : call
 rootpage : 4
 sql : CREATE TABLE call (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, address TEXT, date INTEGER, duration INTEGER, flags INTEGER, id INTEGER, name TEXT, country_code TEXT)

 type : table
 name : sqlite_sequence
 tbl_name : sqlite_sequence
 rootpage : 5
 sql : CREATE TABLE sqlite_sequence(name,seq)

 type : table
 name : data
 tbl_name : data
 rootpage : 6
 sql : CREATE TABLE data (ROWID INTEGER PRIMARY KEY AUTOINCREMENT, pdp_ip INTEGER, bytes_rcvd REAL, bytes_sent REAL, bytes_last_rcvd REAL, bytes_last_sent REAL, bytes_lifetime_rcvd REAL, bytes_lifetime_sent REAL)

More info on other iphone databases can be found on this link:

http://damon.durandfamily.org/archives/000487.html

Accessing the Call History

Now that we know the call history db path and schema, we can see that we are interested in the “call” table. Thanks to the above link, we know what exactly is stored in this table:

ROWID (INTEGER PRIMARY KEY AUTOINCREMENT) :   Auto-incrementing field/counter
address (TEXT) :   International-formatted foreign address 
date (INTEGER) :  OSX-epoch based datetime, convertable via date -r
duration (INTEGER) :  Length of call in seconds rounded to next minute, 0 = missed call
flags (INTEGER) :  Flags controlling the type of record; 5 – Outgoing call | 4 – Incoming call
id (INTEGER) :  AddressBook ID for outgoing calls selected from AddressBook, otherwise -1

Now we should write some code to access that. The following code accesses the “call” table and stores the retrieved values in an array of dictionaries. You could write your own class and use an array of objects of that class:

NSString *callHisoryDatabasePath = @"/private/var/wireless/Library/CallHistory/call_history.db";
BOOL callHistoryFileExist = FALSE;
callHistoryFileExist = [fileManager fileExistsAtPath:callHisoryDatabasePath];
[fileManager release];
NSMutableArray *callHistory = [[NSMutableArray alloc] init];

if(callHistoryFileExist) {
    if ([fileManager isReadableFileAtPath:callHisoryDatabasePath]) {
        sqlite3 *database;
        if(sqlite3_open([callHisoryDatabasePath UTF8String], &database) == SQLITE_OK) {
            sqlite3_stmt *compiledStatement;
            NSString *sqlStatement = [NSString stringWithString:@"SELECT * FROM call;"];

            int errorCode = sqlite3_prepare_v2(database, [sqlStatement UTF8String], -1, 
                                                &compiledStatement, NULL);
            if( errorCode == SQLITE_OK) {
                int count = 1;

                while(sqlite3_step(compiledStatement) == SQLITE_ROW) {
                    // Read the data from the result row
                    NSMutableDictionary *callHistoryItem = [[NSMutableDictionary alloc] init];
                    int numberOfColumns = sqlite3_column_count(compiledStatement);
                    NSString *data;
                    NSString *columnName;

                    for (int i = 0; i < numberOfColumns; i++) {
                        columnName = [[NSString alloc] initWithUTF8String:
                                    (char *)sqlite3_column_name(compiledStatement, i)];
                        data = [[NSString alloc] initWithUTF8String:
                                (char *)sqlite3_column_text(compiledStatement, i)];
    
                        [callHistoryItem setObject:data forKey:columnName];

                        [columnName release];
                        [data release];
                    }
                    [callHistory addObject:callHistoryItem];
                    [callHistoryItem release];
                    count++;
                }
            }
            else {
                NSLog(@"Failed to retrieve table");
                NSLog(@"Error Code: %d", errorCode);
            }
            sqlite3_finalize(compiledStatement);
        }
    }
}

If you want to show date as a real date not number of seconds since epoch use this if-statement:

if (![columnName isEqualToString:@"date"]) {
    [callHistoryItem setObject:data forKey:columnName];
}
else {

    NSDate *callDate = [NSDate dateWithTimeIntervalSince1970:[data intValue]];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateStyle:NSDateFormatterFullStyle];
    [dateFormatter setTimeStyle:NSDateFormatterFullStyle];
    [dateFormatter setTimeZone:[NSTimeZone systemTimeZone]];

[callHistoryItem setObject:[dateFormatter stringFromDate:callDate] forKey:columnName];

}

Other conditions can be added to check the ‘flag’ and determining whether the call is incoming or outgoing.

Dont be afraid of using this in an application for the app store. There is already an app on iTunes that accesses call history.

Note: I found values other than 4 and 5 for flags, like 20,21. I dont know what they mean yet. Ill update as soon as I do. Meanwhile, you guys share what different values you get with us and also if you find out what they mean.

Happy Coding :)

About these ads

83 Comments on “Accessing iPhone Call History”

  1. shankar Phadatare says:

    Hi ,

    I am getting following 3 paths

    var/db/launchd.db
    var/mobile/Library/AddressBook/AddressBook.sqlitedb
    var/mobile/Library/AddressBook/AddressBookImages.sqlitedb

    but not getting following path using your code.
    var/wireless/Library/CallHistory/call_history.db.

    Is there any way to get above path permission to access call log history.

    please reply with solutions

  2. I’d gladly pay $4.99 for a simple app that stores my entire call history in a searchable file somewhere on the iphone. I see lots of ways to back up call history to my desktop, but that’s not nearly as useful.

    Storage could be local on the phone or to a cloud service as an xls file, though the local would be preferable.

    The “100 entries only” limit on the native phone history/call app is just absurd. Apple gave me room for 10,000 songs but only 100 2byte log entries? I’m so surprised someone hasn’t solved this yet. (Did I miss a solution that doesn’t require jailbreaking my phone?)

    Thanks!

  3. Mostbar says:

    This is really helpful. Thank you for sharing that =)

  4. lasseo says:

    Maybe a stupied question, but how do you know which hidden APIs that you are allowed to use and will pass signing? Is it trial and error?

    • xs2bush says:

      No. its not. Its not a hidden API. Its just a db that wasnt accessible prior to iOS4.0. Plus i gave a link to an app that does this and its on the app store (obviously went through the Apple Review process). This code will work on non-jailbreak devices which is also an evidence that its Apple approved.

  5. tabtrend@gmail.com says:

    Hi Bushra,
    I left a message for you in linkedin. I totally love this blog and I learnt a lot of stuff from this blog. Kindly email me at tabtrend at gmail.com . I have a ios project and need your help.
    – Arun

  6. Kevin Cador says:

    Hey,

    Does somebody knows if those files will be accessible on iOS 5?
    This seems like a bigger security/privacy issue than the famous localization database issue!

    Thanks.

    Kevin

    • xs2bush says:

      iOS 5 is under nda. Even if i find out, i couldnt tell you and i would also advice others not to comment on this. But ill post an update as soon as iOS 5 launches :)

      • Jack says:

        Ok. So as an iOS developer you have the new iOS5 and you have probably tried accessing the database. I understand that you are under an NDA and can’t discuss details, so I will wait until it’s officially released. Until that time, should I be worried that this will no longer work?

      • xs2bush says:

        I think not because although this feature can be misused there are no instances of that as yet

      • Jack says:

        Ok. I wasn’t too worried that under iOS5 that the database would be inaccessible, but now I can’t find it.

        I’m starting to worry.

        Any tips on how I can get this to go under ios5?

  7. SEO says:

    I am so happy to read this. This is the kind of info that needs to be given and not the random misinformation that is at the other blogs. Appreciate your sharing this beneficial content.

  8. keshav says:

    every thing is fine, how to apply this in my own app , what are the IB elements to be taken……?????
    can you please help me…..

    • xs2bush says:

      I have shared the main code to start anyone off….unfortunately i cant write ur app for you :)

      • keshav says:

        sir, thankyou for your reply response,, and where to apply this code is my question ,, i mean that , do i need to create a DataBase named “callhistory”
        in my terminal (or wat to do) ??
        how to apply this code in app,( in viewDidLoad method )??? or some wher else??

      • xs2bush says:

        its ma’am, keshav this code is ready to go anyhwhere you like. This code is complete in the sense of accessing the data, how you display it or whether you decide to store it inside your app is totally dependent on you :)

  9. keshav says:

    my task is to access the callLogs(missed,dailed,recieved) into my app, to find out the count of all the Call Logs and to display in my app,,, ,,,
    sir , do i need to create database named (callhistory)or what?
    thanks for your replies…..

  10. keshav says:

    can you please help me ?????

  11. marc says:

    Thanks very much for this! I look forward to your update post after iOS5 is released. I’d love to use this approach in an application I’m building that needs access to the call log, but I’m leery that iOS5 will render this approach unusable.

  12. srini says:

    var/db/launchd.db
    var/mobile/Library/AddressBook/AddressBook.sqlitedb
    var/mobile/Library/AddressBook/AddressBookImages.sqlitedb
    var/wireless/Library/CallHistory/call_history.db

    You got this list by running the script in your iphone device ? I tried it in the simulator and i got different output.

  13. Adrian says:

    I just tried this with IOS5 and i cant see call_history.db anymore. I can only see launchd.db

  14. Dale says:

    Anyone have a solution for iOS 5.0?

  15. eran says:

    this is great!!!!
    very well explained
    thanks a lot man

  16. kimsu says:

    i get values other than 5 & 4 in “flags filed. it is 65540 & 65541. Wonder if they has sth to do with 65536 which is 2^16?

    • koverg says:

      These are bitfields.
      00004 = 00000000000000100
      00005 = 00000000000000101
      65540 = 10000000000000100
      65541 = 10000000000000101

      So you only have to check the bit0 to determine wether it is incoming or outgoing.

      But do you still use 4.3 or could you manage to read these values on iOS5? How?

      • Sikander says:

        Hi koverg,
        can you please tell me what is the memory location of this value i.e
        where are these values stored? please reply

  17. Pantelis Zirinis says:

    Hope sooner or later they release a way to access recent calls.

  18. Hi,
    I want to know that how many calls log data is stored in call_history.db table OR how many days back data is stored in call_history.db?. Is there any limitations for call_history.db table to store the call logs? If have please help me regarding to know call_history.db limitations.

    Waiting for your valuable reply.

    Thanks.

  19. Ramkumar says:

    Hi Guys,

    Is there is any possible to set reminder programatically…? Also I want to know is it possible to set ringtones programatically in non-jail broken devices…?

    Thanks,
    Ram

  20. Ziad says:

    Hi Bushra
    I have an app called calimits which is mainly deals with call history of the iphone.
    My problem now is the stop reading the call log (history) iOS5.
    Please Advice.
    Regards,
    Ziad

  21. Usman says:

    Hi,

    I want to know is there any way by which i can differentiate between calls(Unanswered/Busy/attended) in iPhone. I’ve developed phone like module inside my app which allows user to call directly from the app without quitting(run on background) my app and when call ends, it take the user back to my app(on current view controller). what i want is, to know whether the call i made goes unanswered, busy or attended by the recipient. It’s very necessary for what i am trying to do. what’s your thought on it, is it possible? I’ve done extensive research on it’s possibility and failed to find anything relevant.

    Thanks

  22. Brent says:

    Does iOS 5 block/deny access to *all* /var/mobile/ and /var/wireless/ databases? Or just Call_history.db? For example, can an iPhone app utilize/access the SMS.db or is this toast now as well?

    Thank you.

  23. pawan agarwal says:

    hello sir ,

    this code for very use full .but i con’t show history data in table view call.

    only show data in nslog . plz help me.

  24. pawan agarwal says:

    using this code and geting call histroy in table view cell

    NSString *address;
    NSString *userId;
    NSString *name;
    NSString *ROWID;
    NSString *date;
    NSString *country_code;

    NSDictionary *objdict=[callHistory objectAtIndex:indexPath.row];

    address=[objdict objectForKey:@”address”];
    userId=[objdict objectForKey:@”id”];
    name=[objdict objectForKey:@”name”];
    ROWID=[objdict objectForKey:@”ROWID”];
    date=[objdict objectForKey:@”date”];
    country_code=[objdict objectForKey:@”country_code”];

    int flag=[[objdict objectForKey:@”flags”]intValue];
    if(flag==4)
    {
    //NSLog(@”call is incoming”);
    //NSLog(@”call is incoming duration is: %@”,[callHistoryItem objectForKey:@”duration”]);
    ObjCellHistoryview.CallImage.image=[UIImage imageNamed:@”b.png”];

    }
    else if(flag==5) {
    //NSLog(@”call is outgoing”);
    //NSLog(@”call is outgoing duration is: %@”,[callHistoryItem objectForKey:@”duration”]);
    ObjCellHistoryview.CallImage.image=[UIImage imageNamed:@”o.png”];

    }
    int durations=[[objdict objectForKey:@”duration”]intValue];

    if(durations==0)
    {
    //NSLog(@”call is Misscall”);
    //NSLog(@”duration is: %@”,[objdict objectForKey:@”duration”]);
    ObjCellHistoryview.LblDuration.text =[NSString stringWithFormat:@”%d”,durations];
    ObjCellHistoryview.CallImage.image=[UIImage imageNamed:@”r.png”];
    }

    ObjCellHistoryview.LblHNo.text =[NSString stringWithFormat:@”%@%@”,name,address];
    //ObjCellHistoryview.LblDuration.text =[NSString stringWithFormat:@”%@”,duration];
    ObjCellHistoryview.LblHTM.text =[NSString stringWithFormat:@”%@”,date];

  25. sumit mundra says:

    Thanks very much for this……….

  26. Adel says:

    Thanks for this new info.
    you post very helpful ,

    But do you have any walk-around solution for iOS 5?

  27. Santosh says:

    Hi Guys,
    I have to access callhistory.db in iOS 5 App for calimit App.Any clues.Plz share

  28. ram says:

    hi this is outdated. now for ios 5 it not working. so tell new path for this. But that was nice.

  29. Fulvio says:

    I understand that that is no way to access to the log in IO5 and there is no walk around. My question is: is there a way to get the total amount of sent and get byte transfer?. I can see that information in General – Usage – cellular Networking data Volume.

  30. bhushan says:

    i am newbee can you post code for me….

  31. lighting says:

    Calimits is the answer ;)

  32. Narendra Kumar says:

    I have iphone 3gs with ios5.0.1 upgraded and run your code

    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSDirectoryEnumerator *dirnum = [[NSFileManager defaultManager] enumeratorAtPath: @”/private/”];
    NSString *nextItem = [NSString string];
    while( (nextItem = [dirnum nextObject])) {
    if ([[nextItem pathExtension] isEqualToString: @”db”] ||
    [[nextItem pathExtension] isEqualToString: @”sqlitedb”]) {
    if ([fileManager isReadableFileAtPath:nextItem]) {
    NSLog(@”%@”, nextItem);
    }
    }
    }

    Output:

    2012-04-30 18:11:50.377 CallClass_InMain[1993:707] var/MobileSoftwareUpdate/softwareupdate.232/target/root/System/Library/Frameworks/CoreLocation.framework/Support/factory.db
    2012-04-30 18:11:50.423 CallClass_InMain[1993:707] var/MobileSoftwareUpdate/softwareupdate.232/target/root/System/Library/Frameworks/CoreTelephony.framework/Support/lasd.db
    2012-04-30 18:11:50.431 CallClass_InMain[1993:707] var/MobileSoftwareUpdate/softwareupdate.232/target/root/System/Library/Frameworks/CoreTelephony.framework/Support/plmn.db
    2012-04-30 18:11:55.961 CallClass_InMain[1993:707] var/db/launchd.db
    2012-04-30 18:11:56.279 CallClass_InMain[1993:707] var/root/Library/Caches/Backup/cache.db
    2012-04-30 18:11:56.295 CallClass_InMain[1993:707] var/root/Library/Caches/com.apple.backupd/Cache.db
    2012-04-30 18:11:56.307 CallClass_InMain[1993:707] var/root/Library/Caches/com.apple.coreservices.appleid.agent/Cache.db
    2012-04-30 18:11:56.350 CallClass_InMain[1993:707] var/root/Media/iTunes_Control/iTunes/MediaLibrary.sqlitedb
    2012-04-30 18:11:56.362 CallClass_InMain[1993:707] var/root/Media/iTunes_Control/iTunes/UbiquitousMetadata.sqlitedb

    Didn’t showing any call_history database.
    var/mobile/Library/AddressBook/AddressBookImages.sqlitedb
    var/wireless/Library/CallHistory/call_history.db

    Please help where I am going wrong.

  33. lg says:

    Just for reference, this database is also available in the iTunes backup of the phone. The name is 2b2b0084a1bc3a5ac8c27afdf14afb42c61a19ca.

  34. […] iOS Call History Tutorial Share this:FacebookTwitterLike this:LikeBe the first to like this.   Leave a comment […]

  35. Paulo says:

    Hi is there a way to edit the call log. Iphone 3g 4.2.1

  36. snack maker says:

    I do not even understand how I finished up right here, however I believed this put up was great. I don’t realize who you are but definitely you are going to a famous blogger in case you aren’t already. Cheers!

  37. charlotte says:

    This sample code does not work for ios 6.0 please help

  38. Fantastic site. A lot of helpful information here. I am sending
    it to a few friends ans also sharing in delicious.
    And of course, thank you to your effort!

  39. Ghanshyam says:

    I don’t get /private/var/db/callhistory/call_history.db in iOS 5.0 simulator . i am getting another DB’s log among them one is /private/var/db/launched.db . i am going to access thid using sqlite3 iPhone API then it returns me “Failed to retrieve table”

    Your help is appreciated
    Thanks & Regards
    Ghanshyam Maliwal

  40. Zoeb S says:

    Hey Bushra, any update for ios6 ?


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 55 other followers