AccountBackupRestore.java revision f5418f1f93b02e7fab9f15eb201800b65510998e
1/* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.provider; 18 19import com.android.email.Email; 20import com.android.email.Preferences; 21import com.android.emailcommon.Logging; 22import com.android.emailcommon.provider.Account; 23import com.android.emailcommon.provider.EmailContent; 24 25import android.content.ContentResolver; 26import android.content.Context; 27import android.text.TextUtils; 28import android.util.Log; 29 30/** 31 * Helper class to facilitate EmailProvider's account backup/restore facility. 32 * 33 * Account backup/restore was implemented entirely for the purpose of recovering from database 34 * corruption errors that were/are sporadic and of undetermined cause (though the prevailing wisdom 35 * is that this is due to some kind of memory issue). Rather than have the offending database get 36 * deleted by SQLiteDatabase and forcing the user to recreate his accounts from scratch, it was 37 * decided to backup accounts when created/modified and then restore them if 1) there are no 38 * accounts in the database and 2) there are backup accounts. This, at least, would cause user's 39 * email data for IMAP/EAS to be re-synced and prevent the worst outcomes from occurring. 40 * 41 * To accomplish backup/restore, we use the facility now built in to EmailProvider to store a 42 * backup version of the Account and HostAuth tables in a second database (EmailProviderBackup.db) 43 * 44 * TODO: We might look into having our own DatabaseErrorHandler that tries to be clever about 45 * determining whether or not a "corrupt" database is truly corrupt; the problem here is that it 46 * has proven impossible to reproduce the bug, and therefore any "solution" of this kind of utterly 47 * impossible to test in the wild. 48 */ 49public class AccountBackupRestore { 50 // We only need to do this once, so prevent extra work by remembering this... 51 private static boolean sBackupsChecked = false; 52 53 /** 54 * Backup user Account and HostAuth data into our backup database 55 */ 56 public static void backup(Context context) { 57 ContentResolver resolver = context.getContentResolver(); 58 int numBackedUp = resolver.update(EmailProvider.ACCOUNT_BACKUP_URI, null, null, null); 59 if (numBackedUp < 0) { 60 Log.e(Logging.LOG_TAG, "Account backup failed!"); 61 } else if (Email.DEBUG) { 62 Log.d(Logging.LOG_TAG, "Backed up " + numBackedUp + " accounts..."); 63 } 64 } 65 66 /** 67 * Restore user Account and HostAuth data from our backup database 68 */ 69 public static void restoreIfNeeded(Context context) { 70 if (sBackupsChecked) return; 71 72 // Check for legacy backup 73 String legacyBackup = Preferences.getLegacyBackupPreference(context); 74 // If there's a legacy backup, create a new-style backup and delete the legacy backup 75 // In the 1:1000000000 chance that the user gets an app update just as his database becomes 76 // corrupt, oh well... 77 if (!TextUtils.isEmpty(legacyBackup)) { 78 backup(context); 79 Preferences.clearLegacyBackupPreference(context); 80 Log.w(Logging.LOG_TAG, "Created new EmailProvider backup database"); 81 } 82 83 // If we have accounts, we're done 84 if (EmailContent.count(context, Account.CONTENT_URI) > 0) return; 85 ContentResolver resolver = context.getContentResolver(); 86 int numRecovered = resolver.update(EmailProvider.ACCOUNT_RESTORE_URI, null, null, null); 87 if (numRecovered > 0) { 88 Log.e(Logging.LOG_TAG, "Recovered " + numRecovered + " accounts!"); 89 } else if (numRecovered < 0) { 90 Log.e(Logging.LOG_TAG, "Account recovery failed?"); 91 } else if (Email.DEBUG) { 92 Log.d(Logging.LOG_TAG, "No accounts to restore..."); 93 } 94 sBackupsChecked = true; 95 } 96} 97