Fix ANRs from Email.setServicesEnabled()

* Create sync & async versions
* Rename all callsites so sync is very apparent
* Fix callsites appropriately
* Clean up interaction between reconciler and setServicesEnabled

Bug: 3133770
Bug: 3134677
Change-Id: Iefbc7814d9aa390baea6345e450e2a4768bf0a9a
This commit is contained in:
Andy Stadler 2010-12-23 13:19:55 -08:00
parent 6aec257fe7
commit 2959a7e073
9 changed files with 64 additions and 41 deletions

View File

@ -72,7 +72,7 @@ public class AccountBackupRestore {
// update security profile // update security profile
SecurityPolicy.getInstance(context).updatePolicies(-1); SecurityPolicy.getInstance(context).updatePolicies(-1);
// enable/disable other email services as necessary // enable/disable other email services as necessary
Email.setServicesEnabled(context); Email.setServicesEnabledSync(context);
ExchangeUtils.startExchangeService(context); ExchangeUtils.startExchangeService(context);
} }
sBackupsChecked = true; sBackupsChecked = true;

View File

@ -963,7 +963,7 @@ public class Controller {
// Release or relax device administration, if relevant // Release or relax device administration, if relevant
SecurityPolicy.getInstance(context).reducePolicies(); SecurityPolicy.getInstance(context).reducePolicies();
Email.setServicesEnabled(context); Email.setServicesEnabledSync(context);
} catch (Exception e) { } catch (Exception e) {
Log.w(Email.LOG_TAG, "Exception while deleting account", e); Log.w(Email.LOG_TAG, "Exception while deleting account", e);
} finally { } finally {

View File

@ -175,12 +175,32 @@ public class Email extends Application {
return sTempDirectory; return sTempDirectory;
} }
/**
* Asynchronous version of {@link #setServicesEnabledSync(Context)}. Use when calling from
* UI thread (or lifecycle entry points.)
*
* @param context
*/
public static void setServicesEnabledAsync(final Context context) {
Utility.runAsync(new Runnable() {
@Override
public void run() {
setServicesEnabledSync(context);
}
});
}
/** /**
* Called throughout the application when the number of accounts has changed. This method * Called throughout the application when the number of accounts has changed. This method
* enables or disables the Compose activity, the boot receiver and the service based on * enables or disables the Compose activity, the boot receiver and the service based on
* whether any accounts are configured. Returns true if there are any accounts configured. * whether any accounts are configured.
*
* Blocking call - do not call from UI/lifecycle threads.
*
* @param context
* @return true if there are any accounts configured.
*/ */
public static boolean setServicesEnabled(Context context) { public static boolean setServicesEnabledSync(Context context) {
Cursor c = null; Cursor c = null;
try { try {
c = context.getContentResolver().query( c = context.getContentResolver().query(
@ -197,7 +217,7 @@ public class Email extends Application {
} }
} }
public static void setServicesEnabled(Context context, boolean enabled) { private static void setServicesEnabled(Context context, boolean enabled) {
PackageManager pm = context.getPackageManager(); PackageManager pm = context.getPackageManager();
if (!enabled && pm.getComponentEnabledSetting( if (!enabled && pm.getComponentEnabledSetting(
new ComponentName(context, MailService.class)) == new ComponentName(context, MailService.class)) ==

View File

@ -410,7 +410,7 @@ public class UpgradeAccounts extends ListActivity implements OnClickListener {
} }
// Step 4: Enable app-wide features such as composer, and start mail service(s) // Step 4: Enable app-wide features such as composer, and start mail service(s)
Email.setServicesEnabled(mContext); Email.setServicesEnabledSync(mContext);
} }
return null; return null;

View File

@ -620,12 +620,7 @@ public class AccountSettingsFragment extends PreferenceFragment {
mAccount.update(mContext, cv); mAccount.update(mContext, cv);
// Run the remaining changes off-thread // Run the remaining changes off-thread
Utility.runAsync(new Runnable() { Email.setServicesEnabledAsync(mContext);
@Override
public void run() {
Email.setServicesEnabled(mContext);
}
});
} }
/** /**

View File

@ -223,12 +223,15 @@ public class AccountSetupOptions extends AccountSetupActivity implements OnClick
saveAccountAndFinish(); saveAccountAndFinish();
} }
/**
* STOPSHIP most of this work needs to be async
*/
private void saveAccountAndFinish() { private void saveAccountAndFinish() {
// Clear the incomplete/security hold flag now // Clear the incomplete/security hold flag now
Account account = SetupData.getAccount(); Account account = SetupData.getAccount();
account.mFlags &= ~(Account.FLAGS_INCOMPLETE | Account.FLAGS_SECURITY_HOLD); account.mFlags &= ~(Account.FLAGS_INCOMPLETE | Account.FLAGS_SECURITY_HOLD);
AccountSettingsUtils.commitSettings(this, account); AccountSettingsUtils.commitSettings(this, account);
Email.setServicesEnabled(this); Email.setServicesEnabledSync(this);
AccountSetupNames.actionSetNames(this); AccountSetupNames.actionSetNames(this);
// Start up ExchangeService (if it isn't already running) // Start up ExchangeService (if it isn't already running)
ExchangeUtils.startExchangeService(this); ExchangeUtils.startExchangeService(this);

View File

@ -87,7 +87,7 @@ public class EmailBroadcastProcessorService extends IntentService {
} }
private void enableComponentsIfNecessary() { private void enableComponentsIfNecessary() {
if (Email.setServicesEnabled(this)) { if (Email.setServicesEnabledSync(this)) {
// At least one account exists. // At least one account exists.
// TODO probably we should check if it's a POP/IMAP account. // TODO probably we should check if it's a POP/IMAP account.
MailService.actionReschedule(this); MailService.actionReschedule(this);

View File

@ -1046,36 +1046,34 @@ public class ExchangeService extends Service implements Runnable {
*/ */
public class EasAccountsUpdatedListener implements OnAccountsUpdateListener { public class EasAccountsUpdatedListener implements OnAccountsUpdateListener {
public void onAccountsUpdated(android.accounts.Account[] accounts) { public void onAccountsUpdated(android.accounts.Account[] accounts) {
ExchangeService exchangeService = INSTANCE; final ExchangeService exchangeService = INSTANCE;
if (exchangeService != null) { if (exchangeService != null) {
exchangeService.runAccountReconciler(); Utility.runAsync(new Runnable() {
@Override
public void run() {
exchangeService.runAccountReconcilerSync(exchangeService);
}
});
} }
} }
} }
/** /**
* Non-blocking call to run the account reconciler. * Blocking call to the account reconciler
* Launches a worker thread, so it may be called from UI thread.
*/ */
private void runAccountReconciler() { private void runAccountReconcilerSync(Context context) {
final ExchangeService exchangeService = this; android.accounts.Account[] accountMgrList = AccountManager.get(context)
new Thread() { .getAccountsByType(Email.EXCHANGE_ACCOUNT_MANAGER_TYPE);
@Override synchronized (mAccountList) {
public void run() { // Make sure we have an up-to-date sAccountList. If not (for example, if the
android.accounts.Account[] accountMgrList = AccountManager.get(exchangeService) // service has been destroyed), we would be reconciling against an empty account
.getAccountsByType(Email.EXCHANGE_ACCOUNT_MANAGER_TYPE); // list, which would cause the deletion of all of our accounts
synchronized (mAccountList) { if (mAccountObserver != null) {
// Make sure we have an up-to-date sAccountList. If not (for example, if the mAccountObserver.onAccountChanged();
// service has been destroyed), we would be reconciling against an empty account MailService.reconcileAccountsWithAccountManager(context,
// list, which would cause the deletion of all of our accounts mAccountList, accountMgrList, false, mResolver);
if (mAccountObserver != null) {
mAccountObserver.onAccountChanged();
MailService.reconcileAccountsWithAccountManager(exchangeService,
mAccountList, accountMgrList, false, mResolver);
}
}
} }
}.start(); }
} }
public static void log(String str) { public static void log(String str) {
@ -1752,7 +1750,6 @@ public class ExchangeService extends Service implements Runnable {
@Override @Override
public void onCreate() { public void onCreate() {
synchronized (sSyncLock) { synchronized (sSyncLock) {
Email.setServicesEnabled(this);
alwaysLog("!!! EAS ExchangeService, onCreate"); alwaysLog("!!! EAS ExchangeService, onCreate");
if (sStop) { if (sStop) {
return; return;
@ -1765,9 +1762,17 @@ public class ExchangeService extends Service implements Runnable {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
// Run the reconciler and clean up any mismatched accounts - if we weren't running when // Finally, run some setup activities off the UI thread
// accounts were deleted, it won't have been called. Utility.runAsync(new Runnable() {
runAccountReconciler(); @Override
public void run() {
// Run the reconciler and clean up any mismatched accounts - if we weren't
// running when accounts were deleted, it won't have been called.
runAccountReconcilerSync(ExchangeService.this);
// Update other services depending on final account configuration
Email.setServicesEnabledSync(ExchangeService.this);
}
});
} }
} }

View File

@ -137,7 +137,7 @@ public class MessageComposeTests
} }
Account account = Account.restoreAccountWithId(mContext, accountId); Account account = Account.restoreAccountWithId(mContext, accountId);
mSignature = account.getSignature(); mSignature = account.getSignature();
Email.setServicesEnabled(mContext); Email.setServicesEnabledSync(mContext);
Intent intent = new Intent(Intent.ACTION_VIEW); Intent intent = new Intent(Intent.ACTION_VIEW);
setActivityIntent(intent); setActivityIntent(intent);