iOS获取通话记录

前言


iOS8.3之后,存放通话日志的数据库的位置换成了/private/var/mobile/Library/CallHistoryDB/目录,数据库名为CallHistory.storedata,由于其采用了Sqlitewal机制,所以还有两个文件,分别为CallHistory.storedata-shmCallHistory.storedata-wal,一开始想得很简单,直接使用Sqlite打开该数据库,读取即可,后来发现使用NSFileManager并不能获取到该文件。接下来将详细列出解决思路。

读取通话日志文件


直接使用NSFileManager不能获取到通话日志文件的原因很简单,因为App本身是在一个沙盒里面的,App只能访问自己的存储空间,系统有一套安全机制来对其进行限制。所以,首先得解决这个限制,解决方案是添加Entitlements授权,在Entitlements文件中添加如下代码:

1
2
3
4
5
6
<key>com.apple.security.exception.files.absolute-path.read-write</key>
<array>
<string>/private/var/mobile/Library/CallHistoryDB/CallHistory.storedata-wal</string>
<string>/private/var/mobile/Library/CallHistoryDB/CallHistory.storedata-shm</string>
<string>/private/var/mobile/Library/CallHistoryDB/CallHistory.storedata</string>
</array>

这样,就能访问到通话日志文件。

使用Sqlite读取数据库


使用Sqlite直接读取数据库内容时,发现,在调用int errorCode = sqlite3_prepare_v2(database,[sqlStatement UTF8String],-1,&compiledStatement, NULL);时,出现错误,错误代码为SQLITE_AUTH,既没有权限,最后解决方案是,将3个文件内容读出,并保存到App自己的存储空间中,比如Cache目录中,再对保存到App存储目录中的数据库文件进行操作即可。

解决wal模式问题


由于通话日志的数据库采用了新的事务设计wal(write-ahead logging),数据库修改后,首先会把修改内容写入日志(-wal),为了提高性能创建了一个内存索引(-shm),映射每一个page是否dirty,读取时先看需要的page是否在wal日志中,然后再读取,当达到一定条件后Sqlite会自动将数据flush到数据库文件,当然也可以手动flush,在打开数据库之后,调用sqlite3_wal_checkpoint(database, NULL); 即可手动flush

结束


解决这几个问题后,就能成功读取通话日志了,其他的日志,如短信等等同理都可以读取到。