EmailBroadcastProcessorService.java revision 5c523858385176c33a7456bb84035de78552d22d
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 android.accounts.AccountManager; 20import android.app.IntentService; 21import android.content.ComponentName; 22import android.content.ContentResolver; 23import android.content.ContentUris; 24import android.content.ContentValues; 25import android.content.Context; 26import android.content.Intent; 27import android.content.pm.PackageManager; 28import android.database.Cursor; 29import android.net.Uri; 30import android.util.Log; 31 32import com.android.email.NotificationController; 33import com.android.email.Preferences; 34import com.android.email.R; 35import com.android.email.SecurityPolicy; 36import com.android.email.activity.setup.AccountSettings; 37import com.android.emailcommon.Logging; 38import com.android.emailcommon.VendorPolicyLoader; 39import com.android.emailcommon.provider.Account; 40import com.android.emailcommon.provider.EmailContent.AccountColumns; 41import com.android.emailcommon.provider.HostAuth; 42 43/** 44 * The service that really handles broadcast intents on a worker thread. 45 * 46 * We make it a service, because: 47 * <ul> 48 * <li>So that it's less likely for the process to get killed. 49 * <li>Even if it does, the Intent that have started it will be re-delivered by the system, 50 * and we can start the process again. (Using {@link #setIntentRedelivery}). 51 * </ul> 52 * 53 * This also handles the DeviceAdminReceiver in SecurityPolicy, because it is also 54 * a BroadcastReceiver and requires the same processing semantics. 55 */ 56public class EmailBroadcastProcessorService extends IntentService { 57 // Action used for BroadcastReceiver entry point 58 private static final String ACTION_BROADCAST = "broadcast_receiver"; 59 60 // Dialing "*#*#36245#*#*" to open the debug screen. "36245" = "email" 61 private static final String ACTION_SECRET_CODE = "android.provider.Telephony.SECRET_CODE"; 62 private static final String SECRET_CODE_HOST_DEBUG_SCREEN = "36245"; 63 64 // This is a helper used to process DeviceAdminReceiver messages 65 private static final String ACTION_DEVICE_POLICY_ADMIN = "com.android.email.devicepolicy"; 66 private static final String EXTRA_DEVICE_POLICY_ADMIN = "message_code"; 67 68 // Broadcast received to initiate new message notification updates 69 public static final String ACTION_NOTIFY_NEW_MAIL = 70 "com.android.mail.action.update_notification"; 71 72 public EmailBroadcastProcessorService() { 73 // Class name will be the thread name. 74 super(EmailBroadcastProcessorService.class.getName()); 75 76 // Intent should be redelivered if the process gets killed before completing the job. 77 setIntentRedelivery(true); 78 } 79 80 /** 81 * Entry point for {@link EmailBroadcastReceiver}. 82 */ 83 public static void processBroadcastIntent(Context context, Intent broadcastIntent) { 84 Intent i = new Intent(context, EmailBroadcastProcessorService.class); 85 i.setAction(ACTION_BROADCAST); 86 i.putExtra(Intent.EXTRA_INTENT, broadcastIntent); 87 context.startService(i); 88 } 89 90 /** 91 * Entry point for {@link com.android.email.SecurityPolicy.PolicyAdmin}. These will 92 * simply callback to {@link 93 * com.android.email.SecurityPolicy#onDeviceAdminReceiverMessage(Context, int)}. 94 */ 95 public static void processDevicePolicyMessage(Context context, int message) { 96 Intent i = new Intent(context, EmailBroadcastProcessorService.class); 97 i.setAction(ACTION_DEVICE_POLICY_ADMIN); 98 i.putExtra(EXTRA_DEVICE_POLICY_ADMIN, message); 99 context.startService(i); 100 } 101 102 @Override 103 protected void onHandleIntent(Intent intent) { 104 // This method is called on a worker thread. 105 106 // Dispatch from entry point 107 final String action = intent.getAction(); 108 if (ACTION_BROADCAST.equals(action)) { 109 final Intent broadcastIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT); 110 final String broadcastAction = broadcastIntent.getAction(); 111 112 if (Intent.ACTION_BOOT_COMPLETED.equals(broadcastAction)) { 113 onBootCompleted(); 114 } else if (ACTION_SECRET_CODE.equals(broadcastAction) 115 && SECRET_CODE_HOST_DEBUG_SCREEN.equals(broadcastIntent.getData().getHost())) { 116 AccountSettings.actionSettingsWithDebug(this); 117 } else if (AccountManager.LOGIN_ACCOUNTS_CHANGED_ACTION.equals(broadcastAction)) { 118 onSystemAccountChanged(); 119 } else if (ACTION_NOTIFY_NEW_MAIL.equals(broadcastAction)) { 120 NotificationController.notifyNewMail(this, broadcastIntent); 121 } 122 } else if (ACTION_DEVICE_POLICY_ADMIN.equals(action)) { 123 int message = intent.getIntExtra(EXTRA_DEVICE_POLICY_ADMIN, -1); 124 SecurityPolicy.onDeviceAdminReceiverMessage(this, message); 125 } 126 } 127 128 /** 129 * Handles {@link Intent#ACTION_BOOT_COMPLETED}. Called on a worker thread. 130 */ 131 private void onBootCompleted() { 132 performOneTimeInitialization(); 133 reconcileAndStartServices(); 134 } 135 136 private void reconcileAndStartServices() { 137 // Reconcile accounts 138 MailService.reconcileLocalAccountsSync(this); 139 // Starts remote services, if any 140 EmailServiceUtils.startRemoteServices(this); 141 } 142 143 private void performOneTimeInitialization() { 144 final Preferences pref = Preferences.getPreferences(this); 145 int progress = pref.getOneTimeInitializationProgress(); 146 final int initialProgress = progress; 147 148 if (progress < 1) { 149 Log.i(Logging.LOG_TAG, "Onetime initialization: 1"); 150 progress = 1; 151 if (VendorPolicyLoader.getInstance(this).useAlternateExchangeStrings()) { 152 setComponentEnabled(EasAuthenticatorServiceAlternate.class, true); 153 setComponentEnabled(EasAuthenticatorService.class, false); 154 } 155 } 156 157 if (progress < 2) { 158 Log.i(Logging.LOG_TAG, "Onetime initialization: 2"); 159 progress = 2; 160 setImapDeletePolicy(this); 161 } 162 163 // Add your initialization steps here. 164 // Use "progress" to skip the initializations that's already done before. 165 // Using this preference also makes it safe when a user skips an upgrade. (i.e. upgrading 166 // version N to version N+2) 167 168 if (progress != initialProgress) { 169 pref.setOneTimeInitializationProgress(progress); 170 Log.i(Logging.LOG_TAG, "Onetime initialization: completed."); 171 } 172 } 173 174 /** 175 * Sets the delete policy to the correct value for all IMAP accounts. This will have no 176 * effect on either EAS or POP3 accounts. 177 */ 178 /*package*/ static void setImapDeletePolicy(Context context) { 179 ContentResolver resolver = context.getContentResolver(); 180 Cursor c = resolver.query(Account.CONTENT_URI, Account.CONTENT_PROJECTION, 181 null, null, null); 182 try { 183 while (c.moveToNext()) { 184 long recvAuthKey = c.getLong(Account.CONTENT_HOST_AUTH_KEY_RECV_COLUMN); 185 HostAuth recvAuth = HostAuth.restoreHostAuthWithId(context, recvAuthKey); 186 String legacyImapProtocol = context.getString(R.string.protocol_legacy_imap); 187 if (legacyImapProtocol.equals(recvAuth.mProtocol)) { 188 int flags = c.getInt(Account.CONTENT_FLAGS_COLUMN); 189 flags &= ~Account.FLAGS_DELETE_POLICY_MASK; 190 flags |= Account.DELETE_POLICY_ON_DELETE << Account.FLAGS_DELETE_POLICY_SHIFT; 191 ContentValues cv = new ContentValues(); 192 cv.put(AccountColumns.FLAGS, flags); 193 long accountId = c.getLong(Account.CONTENT_ID_COLUMN); 194 Uri uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); 195 resolver.update(uri, cv, null, null); 196 } 197 } 198 } finally { 199 c.close(); 200 } 201 } 202 203 private void setComponentEnabled(Class<?> clazz, boolean enabled) { 204 final ComponentName c = new ComponentName(this, clazz.getName()); 205 getPackageManager().setComponentEnabledSetting(c, 206 enabled ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED 207 : PackageManager.COMPONENT_ENABLED_STATE_DISABLED, 208 PackageManager.DONT_KILL_APP); 209 } 210 211 private void onSystemAccountChanged() { 212 Log.i(Logging.LOG_TAG, "System accounts updated."); 213 reconcileAndStartServices(); 214 } 215} 216