PopImapSyncAdapterService.java revision ab6321e2c470596b9d8e4f97a23160788d917590
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.OperationCanceledException; 20import android.app.Service; 21import android.content.AbstractThreadedSyncAdapter; 22import android.content.ContentProviderClient; 23import android.content.ContentResolver; 24import android.content.ContentUris; 25import android.content.ContentValues; 26import android.content.Context; 27import android.content.Intent; 28import android.content.SyncResult; 29import android.database.Cursor; 30import android.net.Uri; 31import android.os.Bundle; 32import android.os.IBinder; 33import android.util.Log; 34 35import com.android.emailcommon.mail.MessagingException; 36import com.android.emailcommon.provider.Account; 37import com.android.emailcommon.provider.EmailContent; 38import com.android.emailcommon.provider.EmailContent.AccountColumns; 39import com.android.emailcommon.provider.EmailContent.Message; 40import com.android.emailcommon.provider.HostAuth; 41import com.android.emailcommon.provider.Mailbox; 42import com.android.emailcommon.service.EmailServiceProxy; 43 44import java.util.ArrayList; 45 46public class PopImapSyncAdapterService extends Service { 47 private static final String TAG = "PopImapSyncAdapterService"; 48 private static SyncAdapterImpl sSyncAdapter = null; 49 private static final Object sSyncAdapterLock = new Object(); 50 51 public PopImapSyncAdapterService() { 52 super(); 53 } 54 55 private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter { 56 private Context mContext; 57 58 public SyncAdapterImpl(Context context) { 59 super(context, true /* autoInitialize */); 60 mContext = context; 61 } 62 63 @Override 64 public void onPerformSync(android.accounts.Account account, Bundle extras, 65 String authority, ContentProviderClient provider, SyncResult syncResult) { 66 try { 67 PopImapSyncAdapterService.performSync(mContext, account, extras, 68 authority, provider, syncResult); 69 } catch (OperationCanceledException e) { 70 } 71 } 72 } 73 74 @Override 75 public void onCreate() { 76 super.onCreate(); 77 synchronized (sSyncAdapterLock) { 78 if (sSyncAdapter == null) { 79 sSyncAdapter = new SyncAdapterImpl(getApplicationContext()); 80 } 81 } 82 } 83 84 @Override 85 public IBinder onBind(Intent intent) { 86 return sSyncAdapter.getSyncAdapterBinder(); 87 } 88 89 private static void sync(Context context, long mailboxId, SyncResult syncResult, 90 boolean uiRefresh) { 91 Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId); 92 if (mailbox == null) return; 93 Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey); 94 if (account == null) return; 95 ContentResolver resolver = context.getContentResolver(); 96 String protocol = account.getProtocol(context); 97 if ((mailbox.mType != Mailbox.TYPE_OUTBOX) && !mailbox.loadsFromServer(protocol)) { 98 // This is an update to a message in a non-syncing mailbox; delete this from the 99 // updates table and return 100 resolver.delete(Message.UPDATED_CONTENT_URI, Message.MAILBOX_KEY + "=?", 101 new String[] {Long.toString(mailbox.mId)}); 102 return; 103 } 104 Log.d(TAG, "Mailbox: " + mailbox.mDisplayName); 105 106 Uri mailboxUri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId); 107 ContentValues values = new ContentValues(); 108 // Set mailbox sync state 109 values.put(Mailbox.UI_SYNC_STATUS, 110 uiRefresh ? EmailContent.SYNC_STATUS_USER : EmailContent.SYNC_STATUS_BACKGROUND); 111 resolver.update(mailboxUri, values, null, null); 112 try { 113 try { 114 if (mailbox.mType == Mailbox.TYPE_OUTBOX) { 115 EmailServiceStub.sendMailImpl(context, account.mId); 116 } else if (protocol.equals(HostAuth.SCHEME_IMAP)) { 117 ImapService.synchronizeMailboxSynchronous(context, account, mailbox); 118 } else { 119 Pop3Service.synchronizeMailboxSynchronous(context, account, mailbox); 120 } 121 } catch (MessagingException e) { 122 int cause = e.getExceptionType(); 123 switch(cause) { 124 case MessagingException.IOERROR: 125 syncResult.stats.numIoExceptions++; 126 break; 127 case MessagingException.AUTHENTICATION_FAILED: 128 syncResult.stats.numAuthExceptions++; 129 break; 130 } 131 } 132 } finally { 133 // Always clear our sync state 134 values.put(Mailbox.UI_SYNC_STATUS, EmailContent.SYNC_STATUS_NONE); 135 resolver.update(mailboxUri, values, null, null); 136 } 137 } 138 139 /** 140 * Partial integration with system SyncManager; we initiate manual syncs upon request 141 */ 142 private static void performSync(Context context, android.accounts.Account account, 143 Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) 144 throws OperationCanceledException { 145 // Find an EmailProvider account with the Account's email address 146 Cursor c = null; 147 try { 148 c = provider.query(com.android.emailcommon.provider.Account.CONTENT_URI, 149 Account.CONTENT_PROJECTION, AccountColumns.EMAIL_ADDRESS + "=?", 150 new String[] {account.name}, null); 151 if (c != null && c.moveToNext()) { 152 Account acct = new Account(); 153 acct.restore(c); 154 if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) { 155 Log.d(TAG, "Upload sync request for " + acct.mDisplayName); 156 // See if any boxes have mail... 157 Cursor updatesCursor = provider.query(Message.UPDATED_CONTENT_URI, 158 new String[] {Message.MAILBOX_KEY}, 159 Message.ACCOUNT_KEY + "=?", 160 new String[] {Long.toString(acct.mId)}, 161 null); 162 if ((updatesCursor == null) || (updatesCursor.getCount() == 0)) return; 163 ArrayList<Long> mailboxesToUpdate = new ArrayList<Long>(); 164 while (updatesCursor.moveToNext()) { 165 Long mailboxId = updatesCursor.getLong(0); 166 if (!mailboxesToUpdate.contains(mailboxId)) { 167 mailboxesToUpdate.add(mailboxId); 168 } 169 } 170 for (long mailboxId: mailboxesToUpdate) { 171 sync(context, mailboxId, syncResult, false); 172 } 173 } else { 174 Log.d(TAG, "Sync request for " + acct.mDisplayName); 175 Log.d(TAG, extras.toString()); 176 long mailboxId = extras.getLong(EmailServiceStub.SYNC_EXTRA_MAILBOX_ID, 177 Mailbox.NO_MAILBOX); 178 boolean isInbox = false; 179 if (mailboxId == Mailbox.NO_MAILBOX) { 180 mailboxId = Mailbox.findMailboxOfType(context, acct.mId, 181 Mailbox.TYPE_INBOX); 182 if (mailboxId == Mailbox.NO_MAILBOX) { 183 // Update folders? 184 EmailServiceProxy service = 185 EmailServiceUtils.getServiceForAccount(context, null, acct.mId); 186 service.updateFolderList(acct.mId); 187 } 188 isInbox = true; 189 } 190 if (mailboxId == Mailbox.NO_MAILBOX) return; 191 boolean uiRefresh = 192 extras.getBoolean(ContentResolver.SYNC_EXTRAS_FORCE, false); 193 sync(context, mailboxId, syncResult, uiRefresh); 194 195 // Outbox is a special case here 196 Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId); 197 if (mailbox.mType == Mailbox.TYPE_OUTBOX) { 198 return; 199 } 200 201 // Convert from minutes to seconds 202 int syncFrequency = acct.mSyncInterval * 60; 203 // Values < 0 are for "never" or "push"; 0 is undefined 204 if (syncFrequency <= 0) return; 205 Bundle ex = new Bundle(); 206 if (!isInbox) { 207 ex.putLong(EmailServiceStub.SYNC_EXTRA_MAILBOX_ID, mailboxId); 208 } 209 Log.d(TAG, "Setting periodic sync for " + acct.mDisplayName + ": " + 210 syncFrequency + " seconds"); 211 ContentResolver.addPeriodicSync(account, authority, ex, syncFrequency); 212 } 213 } 214 } catch (Exception e) { 215 e.printStackTrace(); 216 } finally { 217 if (c != null) { 218 c.close(); 219 } 220 } 221 } 222}