replicant-packages_apps_Email/src/com/android/email/AccountBackupRestore.java

227 lines
9.6 KiB
Java

/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.email;
import com.android.email.service.MailService;
import com.android.emailcommon.AccountManagerTypes;
import com.android.emailcommon.CalendarProviderStub;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.EmailContent;
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.provider.ContactsContract;
import android.util.Log;
/**
* Utility functions to support backup and restore of accounts.
*
* In the short term, this is used to work around local database failures. In the long term,
* this will also support server-side backups, providing support for automatic account restoration
* when switching or replacing phones.
*/
public class AccountBackupRestore {
// This reduces the need for expensive checks to just the first time only/
// synchronize on AccountBackupRestore.class
private static boolean sBackupsChecked = false;
/**
* Backup accounts. Can be called from UI thread (does work in a new thread)
*/
public static void backupAccounts(final Context context) {
if (Email.DEBUG) {
Log.v(Logging.LOG_TAG, "backupAccounts");
}
// Because we typically call this from the UI, let's do the work in a thread
new Thread() {
@Override
public void run() {
doBackupAccounts(context, Preferences.getPreferences(context));
}
}.start();
}
/**
* Restore accounts if needed. This is blocking, and should only be called in specific
* startup/entry points.
*/
public static synchronized void restoreAccountsIfNeeded(final Context context) {
// Quick exit if possible
if (sBackupsChecked) return;
// Don't log here; This is called often.
boolean restored = doRestoreAccounts(context, Preferences.getPreferences(context), false);
if (restored) {
// after restoring accounts, register services appropriately
Log.w(Logging.LOG_TAG, "Register services after restoring accounts");
// update security profile
SecurityPolicy.getInstance(context).updatePolicies(-1);
// enable/disable other email services as necessary
Email.setServicesEnabledSync(context);
ExchangeUtils.startExchangeService(context);
}
sBackupsChecked = true;
}
/**
* Non-UI-Thread worker to backup all accounts
*
* @param context used to access the provider
* @param preferences used to access the backups (provided separately for testability)
*/
/* package */ synchronized static void doBackupAccounts(Context context,
Preferences preferences) {
// 1. Wipe any existing backup accounts
Account[] oldBackups = preferences.getAccounts();
for (Account backup : oldBackups) {
backup.delete(preferences);
}
// 2. Identify the default account (if any). This is required because setting
// the default account flag is lazy,and sometimes we don't have any flags set. We'll
// use this to make it explicit (see loop, below).
// This is also the quick check for "no accounts" (the only case in which the returned
// value is -1) and if so, we can exit immediately.
long defaultAccountId = EmailContent.Account.getDefaultAccountId(context);
if (defaultAccountId == -1) {
return;
}
// 3. Create new backup(s), if any
Cursor c = context.getContentResolver().query(EmailContent.Account.CONTENT_URI,
EmailContent.Account.CONTENT_PROJECTION, null, null, null);
try {
while (c.moveToNext()) {
EmailContent.Account fromAccount =
EmailContent.getContent(c, EmailContent.Account.class);
if (Email.DEBUG) {
Log.v(Logging.LOG_TAG, "Backing up account:" + fromAccount.getDisplayName());
}
Account toAccount = LegacyConversions.makeLegacyAccount(context, fromAccount);
// Determine if contacts are also synced, and if so, record that
if (fromAccount.isEasAccount(context)) {
android.accounts.Account acct = new android.accounts.Account(
fromAccount.mEmailAddress, AccountManagerTypes.TYPE_EXCHANGE);
boolean syncContacts = ContentResolver.getSyncAutomatically(acct,
ContactsContract.AUTHORITY);
if (syncContacts) {
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_SYNC_CONTACTS;
}
boolean syncCalendar = ContentResolver.getSyncAutomatically(acct,
CalendarProviderStub.AUTHORITY);
if (syncCalendar) {
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_SYNC_CALENDAR;
}
boolean syncEmail = ContentResolver.getSyncAutomatically(acct,
EmailContent.AUTHORITY);
if (!syncEmail) {
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_DONT_SYNC_EMAIL;
}
}
// Attachment downloading
if ((fromAccount.mFlags & EmailContent.Account.FLAGS_BACKGROUND_ATTACHMENTS) != 0) {
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_BACKGROUND_ATTACHMENTS;
}
// If this is the default account, mark it as such
if (fromAccount.mId == defaultAccountId) {
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_IS_DEFAULT;
}
// Mark this account as a backup of a Provider account, instead of a legacy
// account to upgrade
toAccount.mBackupFlags |= Account.BACKUP_FLAGS_IS_BACKUP;
toAccount.save(preferences);
}
} finally {
c.close();
}
}
/**
* Restore all accounts. This is blocking.
*
* @param context used to access the provider
* @param preferences used to access the backups (provided separately for testability)
* @return true if accounts were restored (meaning services should be restarted, etc.)
*/
/* package */ synchronized static boolean doRestoreAccounts(Context context,
Preferences preferences, boolean unitTest) {
boolean result = false;
// 1. Quick check - if we have any accounts, get out
int numAccounts = EmailContent.count(context, EmailContent.Account.CONTENT_URI, null, null);
if (numAccounts > 0) {
return result;
}
// 2. Quick check - if no backup accounts, get out
Account[] backups = preferences.getAccounts();
if (backups.length == 0) {
return result;
}
Log.w(Logging.LOG_TAG, "*** Restoring Email Accounts, found " + backups.length);
// 3. Possible lost accounts situation - check for any backups, and restore them
for (Account backupAccount : backups) {
// don't back up any leftover legacy accounts (these are migrated elsewhere).
if ((backupAccount.mBackupFlags & Account.BACKUP_FLAGS_IS_BACKUP) == 0) {
continue;
}
// Restore the account
Log.w(Logging.LOG_TAG, "Restoring account:" + backupAccount.getDescription());
EmailContent.Account toAccount = LegacyConversions.makeAccount(context, backupAccount);
// Mark the default account if this is it
if (0 != (backupAccount.mBackupFlags & Account.BACKUP_FLAGS_IS_DEFAULT)) {
toAccount.setDefaultAccount(true);
}
// Restore attachment flag
if (0 != (backupAccount.mBackupFlags & Account.BACKUP_FLAGS_BACKGROUND_ATTACHMENTS)) {
toAccount.mFlags |= EmailContent.Account.FLAGS_BACKGROUND_ATTACHMENTS;
}
// Note that the sense of the email flag is opposite that of contacts/calendar flags
boolean email =
(backupAccount.mBackupFlags & Account.BACKUP_FLAGS_DONT_SYNC_EMAIL) == 0;
boolean contacts = false;
boolean calendar = false;
// Handle system account first, then save in provider
if (toAccount.isEasAccount(context)) {
contacts = (backupAccount.mBackupFlags & Account.BACKUP_FLAGS_SYNC_CONTACTS) != 0;
calendar = (backupAccount.mBackupFlags & Account.BACKUP_FLAGS_SYNC_CALENDAR) != 0;
}
toAccount.save(context);
// Don't simulate AccountManager in unit tests; this results in an NPE
// The unit tests only check EmailProvider based functionality
if (!unitTest) {
MailService.setupAccountManagerAccount(context, toAccount, email, calendar,
contacts, null);
}
result = true;
}
return result;
}
}