From 898283b6fa4f141d5c2a0831b96b19de11d587b3 Mon Sep 17 00:00:00 2001 From: Makoto Onuki Date: Thu, 27 May 2010 18:29:35 -0700 Subject: [PATCH] Fix ANR in one time initializer and unify BroadccastReceivers. - Merged all three BroadcastReceivers into one. (Changed class name because old ones may have been disabled.) - Use IntentService to perform the tasks in a worker thread. Note the new receiver will never be disabled. We always need to start exchange.SyncManager. Bug 2722155 Bug 2416929 Change-Id: I8241880fc1ee38d85dcdca7e1d46fc2f6b2d375b --- AndroidManifest.xml | 25 +--- src/com/android/email/Email.java | 6 - src/com/android/email/OneTimeInitializer.java | 89 ------------ .../android/email/service/BootReceiver.java | 45 ------ .../EmailBroadcastProcessorService.java | 137 ++++++++++++++++++ .../service/EmailBroadcastReceiver.java} | 15 +- 6 files changed, 148 insertions(+), 169 deletions(-) delete mode 100644 src/com/android/email/OneTimeInitializer.java delete mode 100644 src/com/android/email/service/BootReceiver.java create mode 100644 src/com/android/email/service/EmailBroadcastProcessorService.java rename src/com/android/{exchange/BootReceiver.java => email/service/EmailBroadcastReceiver.java} (69%) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index a21a23d45..e982ec684 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -216,26 +216,18 @@ - - - - - - - + + - + - - - - + - - - - - - Android doesn't offer any mechanism to trigger an app right after installation, so we use the - * BOOT_COMPLETED broadcast intent instead. This means, when the app is upgraded, the - * initialization code here won't run until the device reboots. - */ -public class OneTimeInitializer extends BroadcastReceiver { - - @Override - public void onReceive(Context context, Intent intent) { - if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { - initialize(context); - } - } - - /** - * Perform the one-time initialization. - */ - private void initialize(Context context) { - if (Config.LOGD) { - Log.d(Email.LOG_TAG, "OneTimeInitializer: initializing..."); - } - final Preferences pref = Preferences.getPreferences(context); - int progress = pref.getOneTimeInitializationProgress(); - - if (progress < 1) { - progress = 1; - if (VendorPolicyLoader.getInstance(context).useAlternateExchangeStrings()) { - setComponentEnabled(context, EasAuthenticatorServiceAlternate.class, true); - setComponentEnabled(context, EasAuthenticatorService.class, false); - } - - ExchangeUtils.enableEasCalendarSync(context); - } - - // If we need other initializations in the future... - // - add your initialization code here, and - // - rename this class to something like "OneTimeInitializer2" (and modify AndroidManifest - // accordingly) - // Renaming is necessary because once we disable a component, it won't be automatically - // enabled again even when the app is upgraded. - - // Use "progress" to skip the initializations that's already done before. - // Using this preference also makes it safe when a user skips an upgrade. (i.e. upgrading - // version N to version N+2) - - // Save progress and disable itself. - pref.setOneTimeInitializationProgress(progress); - setComponentEnabled(context, getClass(), false); - } - - private void setComponentEnabled(Context context, Class clazz, boolean enabled) { - final ComponentName c = new ComponentName(context, clazz.getName()); - context.getPackageManager().setComponentEnabledSetting(c, - enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED - : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, - PackageManager.DONT_KILL_APP); - } -} diff --git a/src/com/android/email/service/BootReceiver.java b/src/com/android/email/service/BootReceiver.java deleted file mode 100644 index 880773c2b..000000000 --- a/src/com/android/email/service/BootReceiver.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 2008 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.service; - -import com.android.email.AccountBackupRestore; -import com.android.email.Email; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class BootReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - // Restore accounts, if it has not happened already - AccountBackupRestore.restoreAccountsIfNeeded(context); - - if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) { - // Returns true if there are any accounts - if (Email.setServicesEnabled(context)) { - MailService.actionReschedule(context); - } - } - else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(intent.getAction())) { - MailService.actionCancel(context); - } - else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(intent.getAction())) { - MailService.actionReschedule(context); - } - } -} diff --git a/src/com/android/email/service/EmailBroadcastProcessorService.java b/src/com/android/email/service/EmailBroadcastProcessorService.java new file mode 100644 index 000000000..d1a9b98cf --- /dev/null +++ b/src/com/android/email/service/EmailBroadcastProcessorService.java @@ -0,0 +1,137 @@ +/* + * 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.service; + +import com.android.email.Email; +import com.android.email.ExchangeUtils; +import com.android.email.Preferences; +import com.android.email.VendorPolicyLoader; + +import android.app.IntentService; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.util.Config; +import android.util.Log; + +/** + * The service that really handles broadcast intents on a worker thread. + * + * We make it a service, because: + *
    + *
  • So that it's less likely for the process to get killed. + *
  • Even if it does, the Intent that have started it will be re-delivered by the system, + * and we can start the process again. (Using {@link #setIntentRedelivery}). + *
+ */ +public class EmailBroadcastProcessorService extends IntentService { + public EmailBroadcastProcessorService() { + // Class name will be the thread name. + super(EmailBroadcastProcessorService.class.getName()); + + // Intent should be redelivered if the process gets killed before completing the job. + setIntentRedelivery(true); + } + + /** + * Entry point for {@link EmailBroadcastReceiver}. + */ + public static void processBroadcastIntent(Context context, Intent broadcastIntent) { + Intent i = new Intent(context, EmailBroadcastProcessorService.class); + i.putExtra(Intent.EXTRA_INTENT, broadcastIntent); + context.startService(i); + } + + @Override + protected void onHandleIntent(Intent intent) { + // This method is called on a worker thread. + + final Intent original = intent.getParcelableExtra(Intent.EXTRA_INTENT); + final String action = original.getAction(); + + if (Intent.ACTION_BOOT_COMPLETED.equals(action)) { + onBootCompleted(); + + // TODO: Do a better job when we get ACTION_DEVICE_STORAGE_LOW. + // The code below came from very old code.... + } else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(action)) { + // Stop IMAP/POP3 poll. + MailService.actionCancel(this); + } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(action)) { + enableComponentsIfNecessary(); + } + } + + private void enableComponentsIfNecessary() { + if (Email.setServicesEnabled(this)) { + // At least one account exists. + // TODO probably we should check if it's a POP/IMAP account. + MailService.actionReschedule(this); + } + } + + /** + * Handles {@link Intent#ACTION_BOOT_COMPLETED}. Called on a worker thread. + */ + private void onBootCompleted() { + if (Config.LOGD) { + Log.d(Email.LOG_TAG, "BOOT_COMPLETED"); + } + performOneTimeInitialization(); + + enableComponentsIfNecessary(); + + // Starts the service for Exchange, if supported. + ExchangeUtils.startExchangeService(this); + } + + private void performOneTimeInitialization() { + final Preferences pref = Preferences.getPreferences(this); + int progress = pref.getOneTimeInitializationProgress(); + final int initialProgress = progress; + + if (progress < 1) { + Log.i(Email.LOG_TAG, "Onetime initialization: 1"); + progress = 1; + if (VendorPolicyLoader.getInstance(this).useAlternateExchangeStrings()) { + setComponentEnabled(EasAuthenticatorServiceAlternate.class, true); + setComponentEnabled(EasAuthenticatorService.class, false); + } + + ExchangeUtils.enableEasCalendarSync(this); + } + + // Add your initialization steps here. + // Use "progress" to skip the initializations that's already done before. + // Using this preference also makes it safe when a user skips an upgrade. (i.e. upgrading + // version N to version N+2) + + if (progress != initialProgress) { + pref.setOneTimeInitializationProgress(progress); + Log.i(Email.LOG_TAG, "Onetime initialization: completed."); + } + } + + private void setComponentEnabled(Class clazz, boolean enabled) { + final ComponentName c = new ComponentName(this, clazz.getName()); + getPackageManager().setComponentEnabledSetting(c, + enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED + : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, + PackageManager.DONT_KILL_APP); + } +} diff --git a/src/com/android/exchange/BootReceiver.java b/src/com/android/email/service/EmailBroadcastReceiver.java similarity index 69% rename from src/com/android/exchange/BootReceiver.java rename to src/com/android/email/service/EmailBroadcastReceiver.java index 1ebfa7bf1..ce7221043 100644 --- a/src/com/android/exchange/BootReceiver.java +++ b/src/com/android/email/service/EmailBroadcastReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 The Android Open Source Project + * 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. @@ -14,19 +14,18 @@ * limitations under the License. */ -package com.android.exchange; - -import com.android.email.ExchangeUtils; +package com.android.email.service; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.util.Log; -public class BootReceiver extends BroadcastReceiver { +/** + * The broadcast receiver. The actual job is done in EmailBroadcastProcessor on a worker thread. + */ +public class EmailBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - Log.d("Exchange", "BootReceiver onReceive"); - ExchangeUtils.startExchangeService(context); + EmailBroadcastProcessorService.processBroadcastIntent(context, intent); } }