čtvrtek 3. července 2014

Writing to database with locked iPhone.

Our team works now on application called Babel, which allows users to write encrypted messages to each other. They can send either SMS or data messages. For sending data messages, server is needed (it’s part of Babel Business Edition) and client applications communicate with server through xmpp protocol. Right now, we have got client applications for iOS and Android on store.
When message is received in Babel, we store it in database and also send delivery confirmation to sender. When we test our application, everything worked fine, but then, some tester come with an issue - delivery confirmations sometimes doesn’t work. We noticed, that the only difference was, that this iPhone that didn’t send delivery information was locked with passcode. In log, we saw error messages like:
Core Data: error: -executeRequest: encountered exception = Updating max pk failed: unable to open database file with userInfo = {
    NSSQLiteErrorDomain = 14;
}
Core Data: error: -executeRequest: encountered exception = Updating max pk failed: unable to open database file with userInfo = {
    NSSQLiteErrorDomain = 14;
}
Core Data: error: -executeRequest: encountered exception = Updating max pk failed: unable to open database file with userInfo = {
    NSSQLiteErrorDomain = 14;
}
Unresolved error Error Domain=NSCocoaErrorDomain Code=134030 "The operation couldn’t be completed. (Cocoa error 134030.)" UserInfo=0x170079440 {NSSQLiteErrorDomain=14, NSUnderlyingException=Updating max pk failed: unable to open database file}, {
    NSSQLiteErrorDomain = 14;
    NSUnderlyingException = "Updating max pk failed: unable to open database file";
}
Our first idea was simple, problem is, that we protect our database file with protection class NSFileProtectionComplete, which cause that application can not write to this file 10 seconds after passcode locking. So we should change it to protect database files with protection class NSFileProtectionCompleteUnlessOpen.  Flags for initialization of NSPersistentStoreCoordinator looked like this:
NSDictionary* options = @{NSMigratePersistentStoresAutomaticallyOption:@NO, NSSQLitePragmasOption:@{@"journal_mode":@"DELETE”}, NSPersistentStoreFileProtectionKey:NSFileProtectionCompleteUnlessOpen};
But it still didn’t work, error was the same.
After couple of attempts, we noticed, that if we change journal mode from DELETE to WAL, everything works fine. Reason was, that SQLite with journal_mode DELETE creates journal file at the beginning of transaction - that was the problem, this creation of file failed when phone was locked with passcode. WAL journal mode creates all database files at the beginning, so this problem doesn’t exist.
So if you want to write to your database with iPhone locked by passcode, you have these options:
1. Use WAL journal mode and protection class NSFileProtectionCompleteUnlessOpen.
2. Use DELETE journal mode, but because during transaction journal file is created - it has to be created with NSFileProtectionCompleteUntilFirstUserAuthentication protection class, so you have to set this global flag for your app in developer.apple.com
3. Write it to temporary file protected with NSFileProtectionCompleteUntilFirstUserAuthentication and after unlock merge it to database file protected by NSFileProtectionComplete.

Žádné komentáře:

Okomentovat