227 lines
9.6 KiB
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;
|
|
}
|
|
}
|