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