EmailBroadcastProcessorService.java revision f5418f1f93b02e7fab9f15eb201800b65510998e
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.email.service; 18 19import com.android.email.Email; 20import com.android.email.ExchangeUtils; 21import com.android.email.Preferences; 22import com.android.email.SecurityPolicy; 23import com.android.email.VendorPolicyLoader; 24import com.android.email.activity.setup.AccountSettings; 25import com.android.email.mail.Store; 26import com.android.email.widget.WidgetManager; 27import com.android.emailcommon.Logging; 28import com.android.emailcommon.provider.Account; 29import com.android.emailcommon.provider.EmailContent.AccountColumns; 30import com.android.emailcommon.provider.HostAuth; 31 32import android.accounts.AccountManager; 33import android.app.IntentService; 34import android.content.ComponentName; 35import android.content.ContentResolver; 36import android.content.ContentUris; 37import android.content.ContentValues; 38import android.content.Context; 39import android.content.Intent; 40import android.content.pm.PackageManager; 41import android.database.Cursor; 42import android.net.Uri; 43import android.util.Log; 44 45/** 46 * The service that really handles broadcast intents on a worker thread. 47 * 48 * We make it a service, because: 49 * <ul> 50 * <li>So that it's less likely for the process to get killed. 51 * <li>Even if it does, the Intent that have started it will be re-delivered by the system, 52 * and we can start the process again. (Using {@link #setIntentRedelivery}). 53 * </ul> 54 * 55 * This also handles the DeviceAdminReceiver in SecurityPolicy, because it is also 56 * a BroadcastReceiver and requires the same processing semantics. 57 */ 58public class EmailBroadcastProcessorService extends IntentService { 59 // Action used for BroadcastReceiver entry point 60 private static final String ACTION_BROADCAST = "broadcast_receiver"; 61 62 // Dialing "*#*#36245#*#*" to open the debug screen. "36245" = "email" 63 private static final String ACTION_SECRET_CODE = "android.provider.Telephony.SECRET_CODE"; 64 private static final String SECRET_CODE_HOST_DEBUG_SCREEN = "36245"; 65 66 // This is a helper used to process DeviceAdminReceiver messages 67 private static final String ACTION_DEVICE_POLICY_ADMIN = "com.android.email.devicepolicy"; 68 private static final String EXTRA_DEVICE_POLICY_ADMIN = "message_code"; 69 70 public EmailBroadcastProcessorService() { 71 // Class name will be the thread name. 72 super(EmailBroadcastProcessorService.class.getName()); 73 74 // Intent should be redelivered if the process gets killed before completing the job. 75 setIntentRedelivery(true); 76 } 77 78 /** 79 * Entry point for {@link EmailBroadcastReceiver}. 80 */ 81 public static void processBroadcastIntent(Context context, Intent broadcastIntent) { 82 Intent i = new Intent(context, EmailBroadcastProcessorService.class); 83 i.setAction(ACTION_BROADCAST); 84 i.putExtra(Intent.EXTRA_INTENT, broadcastIntent); 85 context.startService(i); 86 } 87 88 /** 89 * Entry point for {@link com.android.email.SecurityPolicy.PolicyAdmin}. These will 90 * simply callback to {@link 91 * com.android.email.SecurityPolicy#onDeviceAdminReceiverMessage(Context, int)}. 92 */ 93 public static void processDevicePolicyMessage(Context context, int message) { 94 Intent i = new Intent(context, EmailBroadcastProcessorService.class); 95 i.setAction(ACTION_DEVICE_POLICY_ADMIN); 96 i.putExtra(EXTRA_DEVICE_POLICY_ADMIN, message); 97 context.startService(i); 98 } 99 100 @Override 101 protected void onHandleIntent(Intent intent) { 102 // This method is called on a worker thread. 103 104 // Dispatch from entry point 105 final String action = intent.getAction(); 106 if (ACTION_BROADCAST.equals(action)) { 107 final Intent broadcastIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT); 108 final String broadcastAction = broadcastIntent.getAction(); 109 110 if (Intent.ACTION_BOOT_COMPLETED.equals(broadcastAction)) { 111 onBootCompleted(); 112 113 // TODO: Do a better job when we get ACTION_DEVICE_STORAGE_LOW. 114 // The code below came from very old code.... 115 } else if (Intent.ACTION_DEVICE_STORAGE_LOW.equals(broadcastAction)) { 116 // Stop IMAP/POP3 poll. 117 MailService.actionCancel(this); 118 } else if (Intent.ACTION_DEVICE_STORAGE_OK.equals(broadcastAction)) { 119 enableComponentsIfNecessary(); 120 } else if (ACTION_SECRET_CODE.equals(broadcastAction) 121 && SECRET_CODE_HOST_DEBUG_SCREEN.equals(broadcastIntent.getData().getHost())) { 122 AccountSettings.actionSettingsWithDebug(this); 123 } else if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(broadcastAction)) { 124 onSystemAccountChanged(); 125 } 126 } else if (ACTION_DEVICE_POLICY_ADMIN.equals(action)) { 127 int message = intent.getIntExtra(EXTRA_DEVICE_POLICY_ADMIN, -1); 128 SecurityPolicy.onDeviceAdminReceiverMessage(this, message); 129 } 130 } 131 132 private void enableComponentsIfNecessary() { 133 if (Email.setServicesEnabledSync(this)) { 134 // At least one account exists. 135 // TODO probably we should check if it's a POP/IMAP account. 136 MailService.actionReschedule(this); 137 } 138 } 139 140 /** 141 * Handles {@link Intent#ACTION_BOOT_COMPLETED}. Called on a worker thread. 142 */ 143 private void onBootCompleted() { 144 performOneTimeInitialization(); 145 146 enableComponentsIfNecessary(); 147 148 // Starts the service for Exchange, if supported. 149 ExchangeUtils.startExchangeService(this); 150 } 151 152 private void performOneTimeInitialization() { 153 final Preferences pref = Preferences.getPreferences(this); 154 int progress = pref.getOneTimeInitializationProgress(); 155 final int initialProgress = progress; 156 157 if (progress < 1) { 158 Log.i(Logging.LOG_TAG, "Onetime initialization: 1"); 159 progress = 1; 160 if (VendorPolicyLoader.getInstance(this).useAlternateExchangeStrings()) { 161 setComponentEnabled(EasAuthenticatorServiceAlternate.class, true); 162 setComponentEnabled(EasAuthenticatorService.class, false); 163 } 164 165 ExchangeUtils.enableEasCalendarSync(this); 166 } 167 168 if (progress < 2) { 169 Log.i(Logging.LOG_TAG, "Onetime initialization: 2"); 170 progress = 2; 171 setImapDeletePolicy(this); 172 } 173 174 // Add your initialization steps here. 175 // Use "progress" to skip the initializations that's already done before. 176 // Using this preference also makes it safe when a user skips an upgrade. (i.e. upgrading 177 // version N to version N+2) 178 179 if (progress != initialProgress) { 180 pref.setOneTimeInitializationProgress(progress); 181 Log.i(Logging.LOG_TAG, "Onetime initialization: completed."); 182 } 183 } 184 185 /** 186 * Sets the delete policy to the correct value for all IMAP accounts. This will have no 187 * effect on either EAS or POP3 accounts. 188 */ 189 /*package*/ static void setImapDeletePolicy(Context context) { 190 ContentResolver resolver = context.getContentResolver(); 191 Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, 192 null, null, null); 193 try { 194 while (c.moveToNext()) { 195 long recvAuthKey = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN); 196 HostAuth recvAuth = HostAuth.restoreHostAuthWithId(context, recvAuthKey); 197 if (Store.STORE_SCHEME_IMAP.equals(recvAuth.mProtocol)) { 198 int flags = c.getInt(Account.CONTENT_FLAGS_COLUMN); 199 flags &= ~Account.FLAGS_DELETE_POLICY_MASK; 200 flags |= Account.DELETE_POLICY_ON_DELETE << Account.FLAGS_DELETE_POLICY_SHIFT; 201 ContentValues cv = new ContentValues(); 202 cv.put(AccountColumns.FLAGS, flags); 203 long accountId = c.getLong(Account.CONTENT_ID_COLUMN); 204 Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); 205 resolver.update(uri, cv, null, null); 206 } 207 } 208 } finally { 209 c.close(); 210 } 211 } 212 213 private void setComponentEnabled(Class<?> clazz, boolean enabled) { 214 final ComponentName c = new ComponentName(this, clazz.getName()); 215 getPackageManager().setComponentEnabledSetting(c, 216 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 217 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 218 PackageManager.DONT_KILL_APP); 219 } 220 221 private void onSystemAccountChanged() { 222 Log.i(Logging.LOG_TAG, "System accounts updated."); 223 MailService.reconcilePopImapAccountsSync(this); 224 225 // If the exchange service wasn't already running, starting it will cause exchange account 226 // reconciliation to be performed. The service stops itself it there are no EAS accounts. 227 ExchangeUtils.startExchangeService(this); 228 } 229} 230