replicant-packages_apps_Email/src/com/android/email/provider/AccountReconciler.java
Scott Kennedy 560bfadc31 Unify LogTag
There is now only one LogTag class. The static initializer of
GmailApplication (existing) and EmailApplication (new) will now set
the log tag to "Gmail" and "Email", respectively. Up until that code
is run, it will be "UnifiedEmail".

"setprop log.tag.Gmail VERBOSE" (or .Email) will trigger all logs to
be printed as long as they go through LogUtils, regardless of what tag
is used by that individual log. This lets us still turn on logging
everywhere in one command, but also lets us use more descriptive tags
(like the class name).

And since we no longer have three com.android.mail.utils.LogTag
classes, builds will be much easier.

Also, we now use LogUtils everywhere.

Change-Id: I55f1c7a66ce50ead54877a13e40256422a56dc39
2013-06-05 20:06:15 -07:00

186 lines
8.4 KiB
Java

/*
* 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.
*/
package com.android.email.provider;
import android.accounts.AccountManager;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.content.Context;
import android.net.Uri;
import com.android.email.NotificationController;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.EmailContent;
import com.android.emailcommon.provider.Mailbox;
import com.android.mail.utils.LogUtils;
import com.google.common.annotations.VisibleForTesting;
import java.io.IOException;
import java.util.List;
public class AccountReconciler {
// 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
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 */);
}
/**
* 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
* @param providerContext application provider context
*/
public static void reconcileAccounts(
Context context,
List<Account> emailProviderAccounts,
android.accounts.Account[] accountManagerAccounts,
Context providerContext) {
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;
// 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) {
// 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) {
LogUtils.w(Logging.LOG_TAG,
"Account reconciler found wrongly incomplete account");
} else {
LogUtils.w(Logging.LOG_TAG,
"Account reconciler noticed incomplete account; ignoring");
continue;
}
}
needsReconciling = true;
if (performReconciliation) {
// This account has been deleted in the AccountManager!
LogUtils.d(Logging.LOG_TAG,
"Account deleted in AccountManager; deleting from provider: " +
providerAccountName);
Uri uri = EmailProvider.uiUri("uiaccount", providerAccount.mId);
context.getContentResolver().delete(uri, null, null);
// Cancel all notifications for this account
NotificationController.cancelNotifications(context, providerAccount);
}
}
}
// 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;
}
}
if (accountManagerAccountName.startsWith(ACCOUNT_MANAGER_ACCOUNT_TEST_PREFIX)) {
found = true;
}
if (!found) {
// This account has been deleted from the EmailProvider database
needsReconciling = true;
if (performReconciliation) {
LogUtils.d(Logging.LOG_TAG,
"Account deleted from provider; deleting from AccountManager: " +
accountManagerAccountName);
// Delete the account
AccountManagerFuture<Boolean> blockingResult = AccountManager.get(context)
.removeAccount(accountManagerAccount, null, null);
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) {
LogUtils.w(Logging.LOG_TAG, e.toString());
} catch (AuthenticatorException e) {
LogUtils.w(Logging.LOG_TAG, e.toString());
} catch (IOException e) {
LogUtils.w(Logging.LOG_TAG, e.toString());
}
}
}
}
return needsReconciling;
}
}