2011-02-09 01:50:30 +00:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2011 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.
|
|
|
|
*/
|
|
|
|
|
2011-07-19 00:33:38 +00:00
|
|
|
package com.android.email.provider;
|
2011-02-09 01:50:30 +00:00
|
|
|
|
|
|
|
import android.accounts.AccountManager;
|
|
|
|
import android.accounts.AccountManagerFuture;
|
|
|
|
import android.accounts.AuthenticatorException;
|
|
|
|
import android.accounts.OperationCanceledException;
|
|
|
|
import android.content.Context;
|
2012-06-28 17:40:46 +00:00
|
|
|
import android.net.Uri;
|
2011-02-09 01:50:30 +00:00
|
|
|
|
2013-05-23 01:36:07 +00:00
|
|
|
import com.android.email.NotificationController;
|
2011-07-19 00:33:38 +00:00
|
|
|
import com.android.emailcommon.Logging;
|
|
|
|
import com.android.emailcommon.provider.Account;
|
2012-09-21 18:27:19 +00:00
|
|
|
import com.android.emailcommon.provider.EmailContent;
|
|
|
|
import com.android.emailcommon.provider.Mailbox;
|
2013-05-26 04:32:32 +00:00
|
|
|
import com.android.mail.utils.LogUtils;
|
2011-07-19 00:33:38 +00:00
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
|
|
|
2011-02-09 01:50:30 +00:00
|
|
|
import java.io.IOException;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
public class AccountReconciler {
|
2011-05-19 21:14:14 +00:00
|
|
|
// AccountManager accounts with a name beginning with this constant are ignored for purposes
|
|
|
|
// of reconcilation. This is for unit test purposes only; the caller may NOT be in the same
|
|
|
|
// package as this class, so we make the constant public.
|
|
|
|
@VisibleForTesting
|
2011-09-15 22:18:26 +00:00
|
|
|
static final String ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX = " _";
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Checks two account lists to see if there is any reconciling to be done. Can be done on the
|
|
|
|
* UI thread.
|
|
|
|
* @param context the app context
|
|
|
|
* @param emailProviderAccounts accounts as reported in the Email provider
|
|
|
|
* @param accountManagerAccounts accounts as reported by the system account manager, for the
|
|
|
|
* particular protocol types that match emailProviderAccounts
|
|
|
|
*/
|
|
|
|
public static boolean accountsNeedReconciling(
|
|
|
|
final Context context,
|
|
|
|
List<Account> emailProviderAccounts,
|
|
|
|
android.accounts.Account[] accountManagerAccounts) {
|
|
|
|
|
|
|
|
return reconcileAccountsInternal(
|
|
|
|
context, emailProviderAccounts, accountManagerAccounts,
|
|
|
|
context, false /* performReconciliation */);
|
|
|
|
}
|
2011-05-19 21:14:14 +00:00
|
|
|
|
2011-02-09 01:50:30 +00:00
|
|
|
/**
|
|
|
|
* Compare our account list (obtained from EmailProvider) with the account list owned by
|
|
|
|
* AccountManager. If there are any orphans (an account in one list without a corresponding
|
|
|
|
* account in the other list), delete the orphan, as these must remain in sync.
|
|
|
|
*
|
|
|
|
* Note that the duplication of account information is caused by the Email application's
|
|
|
|
* incomplete integration with AccountManager.
|
|
|
|
*
|
|
|
|
* This function may not be called from the main/UI thread, because it makes blocking calls
|
|
|
|
* into the account manager.
|
|
|
|
*
|
|
|
|
* @param context The context in which to operate
|
|
|
|
* @param emailProviderAccounts the exchange provider accounts to work from
|
|
|
|
* @param accountManagerAccounts The account manager accounts to work from
|
2011-07-20 18:27:11 +00:00
|
|
|
* @param providerContext application provider context
|
2011-02-09 01:50:30 +00:00
|
|
|
*/
|
2011-09-15 22:18:26 +00:00
|
|
|
public static void reconcileAccounts(
|
|
|
|
Context context,
|
|
|
|
List<Account> emailProviderAccounts,
|
|
|
|
android.accounts.Account[] accountManagerAccounts,
|
2011-07-19 00:33:38 +00:00
|
|
|
Context providerContext) {
|
2011-09-15 22:18:26 +00:00
|
|
|
reconcileAccountsInternal(
|
|
|
|
context, emailProviderAccounts, accountManagerAccounts,
|
|
|
|
providerContext, true /* performReconciliation */);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Internal method to actually perform reconciliation, or simply check that it needs to be done
|
|
|
|
* and avoid doing any heavy work, depending on the value of the passed in
|
|
|
|
* {@code performReconciliation}.
|
|
|
|
*/
|
|
|
|
private static boolean reconcileAccountsInternal(
|
|
|
|
Context context,
|
|
|
|
List<Account> emailProviderAccounts,
|
|
|
|
android.accounts.Account[] accountManagerAccounts,
|
|
|
|
Context providerContext,
|
|
|
|
boolean performReconciliation) {
|
|
|
|
boolean needsReconciling = false;
|
|
|
|
|
2011-02-09 01:50:30 +00:00
|
|
|
// First, look through our EmailProvider accounts to make sure there's a corresponding
|
|
|
|
// AccountManager account
|
|
|
|
for (Account providerAccount: emailProviderAccounts) {
|
|
|
|
String providerAccountName = providerAccount.mEmailAddress;
|
|
|
|
boolean found = false;
|
|
|
|
for (android.accounts.Account accountManagerAccount: accountManagerAccounts) {
|
|
|
|
if (accountManagerAccount.name.equalsIgnoreCase(providerAccountName)) {
|
|
|
|
found = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!found) {
|
|
|
|
if ((providerAccount.mFlags & Account.FLAGS_INCOMPLETE) != 0) {
|
2012-09-21 18:27:19 +00:00
|
|
|
// Do another test before giving up; an incomplete account shouldn't have
|
|
|
|
// any mailboxes (the incomplete flag is used to prevent reconciliation
|
|
|
|
// between the time the EP account is created and when the AM account is
|
|
|
|
// asynchronously created)
|
|
|
|
if (EmailContent.count(providerContext, Mailbox.CONTENT_URI,
|
|
|
|
Mailbox.ACCOUNT_KEY + "=?",
|
|
|
|
new String[] { Long.toString(providerAccount.mId) } ) > 0) {
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.w(Logging.LOG_TAG,
|
2012-09-21 18:27:19 +00:00
|
|
|
"Account reconciler found wrongly incomplete account");
|
|
|
|
} else {
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.w(Logging.LOG_TAG,
|
2012-09-21 18:27:19 +00:00
|
|
|
"Account reconciler noticed incomplete account; ignoring");
|
|
|
|
continue;
|
|
|
|
}
|
2011-02-09 01:50:30 +00:00
|
|
|
}
|
2011-09-15 22:18:26 +00:00
|
|
|
|
|
|
|
needsReconciling = true;
|
|
|
|
if (performReconciliation) {
|
|
|
|
// This account has been deleted in the AccountManager!
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.d(Logging.LOG_TAG,
|
2011-09-15 22:18:26 +00:00
|
|
|
"Account deleted in AccountManager; deleting from provider: " +
|
|
|
|
providerAccountName);
|
2012-06-28 17:40:46 +00:00
|
|
|
Uri uri = EmailProvider.uiUri("uiaccount", providerAccount.mId);
|
|
|
|
context.getContentResolver().delete(uri, null, null);
|
2013-05-23 01:36:07 +00:00
|
|
|
|
|
|
|
// Cancel all notifications for this account
|
|
|
|
NotificationController.cancelNotifications(context, providerAccount);
|
2011-09-15 22:18:26 +00:00
|
|
|
}
|
2011-02-09 01:50:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// Now, look through AccountManager accounts to make sure we have a corresponding cached EAS
|
|
|
|
// account from EmailProvider
|
|
|
|
for (android.accounts.Account accountManagerAccount: accountManagerAccounts) {
|
|
|
|
String accountManagerAccountName = accountManagerAccount.name;
|
|
|
|
boolean found = false;
|
|
|
|
for (Account cachedEasAccount: emailProviderAccounts) {
|
|
|
|
if (cachedEasAccount.mEmailAddress.equalsIgnoreCase(accountManagerAccountName)) {
|
|
|
|
found = true;
|
|
|
|
}
|
|
|
|
}
|
2011-05-19 21:14:14 +00:00
|
|
|
if (accountManagerAccountName.startsWith(ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX)) {
|
|
|
|
found = true;
|
|
|
|
}
|
2011-02-09 01:50:30 +00:00
|
|
|
if (!found) {
|
|
|
|
// This account has been deleted from the EmailProvider database
|
2011-09-15 22:18:26 +00:00
|
|
|
needsReconciling = true;
|
|
|
|
|
|
|
|
if (performReconciliation) {
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.d(Logging.LOG_TAG,
|
2011-09-15 22:18:26 +00:00
|
|
|
"Account deleted from provider; deleting from AccountManager: " +
|
|
|
|
accountManagerAccountName);
|
|
|
|
// Delete the account
|
|
|
|
AccountManagerFuture<Boolean> blockingResult = AccountManager.get(context)
|
2011-02-09 01:50:30 +00:00
|
|
|
.removeAccount(accountManagerAccount, null, null);
|
2011-09-15 22:18:26 +00:00
|
|
|
try {
|
|
|
|
// Note: All of the potential errors from removeAccount() are simply logged
|
|
|
|
// here, as there is nothing to actually do about them.
|
|
|
|
blockingResult.getResult();
|
|
|
|
} catch (OperationCanceledException e) {
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.w(Logging.LOG_TAG, e.toString());
|
2011-09-15 22:18:26 +00:00
|
|
|
} catch (AuthenticatorException e) {
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.w(Logging.LOG_TAG, e.toString());
|
2011-09-15 22:18:26 +00:00
|
|
|
} catch (IOException e) {
|
2013-05-26 04:32:32 +00:00
|
|
|
LogUtils.w(Logging.LOG_TAG, e.toString());
|
2011-09-15 22:18:26 +00:00
|
|
|
}
|
2011-02-09 01:50:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-09-15 22:18:26 +00:00
|
|
|
|
|
|
|
return needsReconciling;
|
2011-02-09 01:50:30 +00:00
|
|
|
}
|
|
|
|
}
|