现在的位置: 首页 > 综合 > 正文

Accessing iPhone Call History(读取iPhone通话记录)

2018年07月15日 ⁄ 综合 ⁄ 共 6758字 ⁄ 字号 评论关闭

    Apple没有提供公开的API给你读取iPhone通话记录,估计今后也不会提供了,尤其在如今更加注重隐私的时候。但是国外的大牛们还是扒出了方法,虽然有些瑕疵(目前貌似只支持IOS4,IOS5之后就失效了)。App Store上确实有款软件“calLog”做到了(当然它在IOS5上无法运行,大家注意看App
Store上的评论,别乱花冤枉钱)。

原文网址:http://iosstuff.wordpress.com/2011/08/19/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.

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.

转载:http://blog.csdn.net/xiaoguan2008/article/details/7465487

抱歉!评论已关闭.