PopImapSyncAdapterService.java revision 1b8e0fa23f6e9957f0b8753dd3f5b95d3f5d98ea
15c523858385176c33a7456bb84035de78552d22dMarc Blank/*
25c523858385176c33a7456bb84035de78552d22dMarc Blank * Copyright (C) 2010 The Android Open Source Project
35c523858385176c33a7456bb84035de78552d22dMarc Blank *
45c523858385176c33a7456bb84035de78552d22dMarc Blank * Licensed under the Apache License, Version 2.0 (the "License");
55c523858385176c33a7456bb84035de78552d22dMarc Blank * you may not use this file except in compliance with the License.
65c523858385176c33a7456bb84035de78552d22dMarc Blank * You may obtain a copy of the License at
75c523858385176c33a7456bb84035de78552d22dMarc Blank *
85c523858385176c33a7456bb84035de78552d22dMarc Blank *      http://www.apache.org/licenses/LICENSE-2.0
95c523858385176c33a7456bb84035de78552d22dMarc Blank *
105c523858385176c33a7456bb84035de78552d22dMarc Blank * Unless required by applicable law or agreed to in writing, software
115c523858385176c33a7456bb84035de78552d22dMarc Blank * distributed under the License is distributed on an "AS IS" BASIS,
125c523858385176c33a7456bb84035de78552d22dMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135c523858385176c33a7456bb84035de78552d22dMarc Blank * See the License for the specific language governing permissions and
145c523858385176c33a7456bb84035de78552d22dMarc Blank * limitations under the License.
155c523858385176c33a7456bb84035de78552d22dMarc Blank */
165c523858385176c33a7456bb84035de78552d22dMarc Blank
175c523858385176c33a7456bb84035de78552d22dMarc Blankpackage com.android.email.service;
185c523858385176c33a7456bb84035de78552d22dMarc Blank
195c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.app.Service;
205c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.content.AbstractThreadedSyncAdapter;
215c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.content.ContentProviderClient;
225c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.content.ContentResolver;
235c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.content.ContentUris;
245c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.content.ContentValues;
255c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.content.Context;
265c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.content.Intent;
275c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.content.SyncResult;
285c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.database.Cursor;
295c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.net.Uri;
305c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.os.Bundle;
315c523858385176c33a7456bb84035de78552d22dMarc Blankimport android.os.IBinder;
325c523858385176c33a7456bb84035de78552d22dMarc Blank
335c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.email.R;
345c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.TempDirectory;
355c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.mail.MessagingException;
365c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.provider.Account;
375c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.provider.EmailContent;
385c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns;
395c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.provider.EmailContent.Message;
405c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.provider.Mailbox;
415c523858385176c33a7456bb84035de78552d22dMarc Blankimport com.android.emailcommon.service.EmailServiceProxy;
42560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedyimport com.android.mail.utils.LogUtils;
435c523858385176c33a7456bb84035de78552d22dMarc Blank
445c523858385176c33a7456bb84035de78552d22dMarc Blankimport java.util.ArrayList;
455c523858385176c33a7456bb84035de78552d22dMarc Blank
465c523858385176c33a7456bb84035de78552d22dMarc Blankpublic class PopImapSyncAdapterService extends Service {
47d1a87bc02d65dde9e635848531e09aadc79ff538Paul Westbrook    private static final String TAG = "PopImapSyncService";
481484c6663db7b194eea9341ec8ecab6b44985c7cYu Ping Hu    private SyncAdapterImpl mSyncAdapter = null;
495c523858385176c33a7456bb84035de78552d22dMarc Blank
505c523858385176c33a7456bb84035de78552d22dMarc Blank    public PopImapSyncAdapterService() {
515c523858385176c33a7456bb84035de78552d22dMarc Blank        super();
525c523858385176c33a7456bb84035de78552d22dMarc Blank    }
535c523858385176c33a7456bb84035de78552d22dMarc Blank
545c523858385176c33a7456bb84035de78552d22dMarc Blank    private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
555c523858385176c33a7456bb84035de78552d22dMarc Blank        public SyncAdapterImpl(Context context) {
565c523858385176c33a7456bb84035de78552d22dMarc Blank            super(context, true /* autoInitialize */);
575c523858385176c33a7456bb84035de78552d22dMarc Blank        }
585c523858385176c33a7456bb84035de78552d22dMarc Blank
595c523858385176c33a7456bb84035de78552d22dMarc Blank        @Override
605c523858385176c33a7456bb84035de78552d22dMarc Blank        public void onPerformSync(android.accounts.Account account, Bundle extras,
615c523858385176c33a7456bb84035de78552d22dMarc Blank                String authority, ContentProviderClient provider, SyncResult syncResult) {
621b8e0fa23f6e9957f0b8753dd3f5b95d3f5d98eaScott Kennedy            PopImapSyncAdapterService.performSync(getContext(), account, extras, provider,
631b8e0fa23f6e9957f0b8753dd3f5b95d3f5d98eaScott Kennedy                    syncResult);
645c523858385176c33a7456bb84035de78552d22dMarc Blank        }
655c523858385176c33a7456bb84035de78552d22dMarc Blank    }
665c523858385176c33a7456bb84035de78552d22dMarc Blank
675c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
685c523858385176c33a7456bb84035de78552d22dMarc Blank    public void onCreate() {
695c523858385176c33a7456bb84035de78552d22dMarc Blank        super.onCreate();
701484c6663db7b194eea9341ec8ecab6b44985c7cYu Ping Hu        mSyncAdapter = new SyncAdapterImpl(getApplicationContext());
715c523858385176c33a7456bb84035de78552d22dMarc Blank    }
725c523858385176c33a7456bb84035de78552d22dMarc Blank
735c523858385176c33a7456bb84035de78552d22dMarc Blank    @Override
745c523858385176c33a7456bb84035de78552d22dMarc Blank    public IBinder onBind(Intent intent) {
751484c6663db7b194eea9341ec8ecab6b44985c7cYu Ping Hu        return mSyncAdapter.getSyncAdapterBinder();
765c523858385176c33a7456bb84035de78552d22dMarc Blank    }
775c523858385176c33a7456bb84035de78552d22dMarc Blank
785c523858385176c33a7456bb84035de78552d22dMarc Blank    /**
795c523858385176c33a7456bb84035de78552d22dMarc Blank     * @return whether or not this mailbox retrieves its data from the server (as opposed to just
805c523858385176c33a7456bb84035de78552d22dMarc Blank     *     a local mailbox that is never synced).
815c523858385176c33a7456bb84035de78552d22dMarc Blank     */
825c523858385176c33a7456bb84035de78552d22dMarc Blank    private static boolean loadsFromServer(Context context, Mailbox m, String protocol) {
835c523858385176c33a7456bb84035de78552d22dMarc Blank        String legacyImapProtocol = context.getString(R.string.protocol_legacy_imap);
8454347010fbbdd3ae1dea5b0e282514c640e16a5fMarc Blank        String pop3Protocol = context.getString(R.string.protocol_pop3);
855c523858385176c33a7456bb84035de78552d22dMarc Blank        if (legacyImapProtocol.equals(protocol)) {
865c523858385176c33a7456bb84035de78552d22dMarc Blank            // TODO: actually use a sync flag when creating the mailboxes. Right now we use an
875c523858385176c33a7456bb84035de78552d22dMarc Blank            // approximation for IMAP.
885c523858385176c33a7456bb84035de78552d22dMarc Blank            return m.mType != Mailbox.TYPE_DRAFTS
895c523858385176c33a7456bb84035de78552d22dMarc Blank                    && m.mType != Mailbox.TYPE_OUTBOX
905c523858385176c33a7456bb84035de78552d22dMarc Blank                    && m.mType != Mailbox.TYPE_SEARCH;
915c523858385176c33a7456bb84035de78552d22dMarc Blank
9254347010fbbdd3ae1dea5b0e282514c640e16a5fMarc Blank        } else if (pop3Protocol.equals(protocol)) {
935c523858385176c33a7456bb84035de78552d22dMarc Blank            return Mailbox.TYPE_INBOX == m.mType;
945c523858385176c33a7456bb84035de78552d22dMarc Blank        }
955c523858385176c33a7456bb84035de78552d22dMarc Blank
965c523858385176c33a7456bb84035de78552d22dMarc Blank        return false;
975c523858385176c33a7456bb84035de78552d22dMarc Blank    }
985c523858385176c33a7456bb84035de78552d22dMarc Blank
995c523858385176c33a7456bb84035de78552d22dMarc Blank    private static void sync(Context context, long mailboxId, SyncResult syncResult,
10017d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu            boolean uiRefresh, int deltaMessageCount) {
1015c523858385176c33a7456bb84035de78552d22dMarc Blank        TempDirectory.setTempDirectory(context);
1025c523858385176c33a7456bb84035de78552d22dMarc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
1035c523858385176c33a7456bb84035de78552d22dMarc Blank        if (mailbox == null) return;
1045c523858385176c33a7456bb84035de78552d22dMarc Blank        Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey);
1055c523858385176c33a7456bb84035de78552d22dMarc Blank        if (account == null) return;
1065c523858385176c33a7456bb84035de78552d22dMarc Blank        ContentResolver resolver = context.getContentResolver();
1075c523858385176c33a7456bb84035de78552d22dMarc Blank        String protocol = account.getProtocol(context);
1085c523858385176c33a7456bb84035de78552d22dMarc Blank        if ((mailbox.mType != Mailbox.TYPE_OUTBOX) &&
1095c523858385176c33a7456bb84035de78552d22dMarc Blank                !loadsFromServer(context, mailbox, protocol)) {
1105c523858385176c33a7456bb84035de78552d22dMarc Blank            // This is an update to a message in a non-syncing mailbox; delete this from the
1115c523858385176c33a7456bb84035de78552d22dMarc Blank            // updates table and return
1125c523858385176c33a7456bb84035de78552d22dMarc Blank            resolver.delete(Message.UPDATED_CONTENT_URI, Message.MAILBOX_KEY + "=?",
1135c523858385176c33a7456bb84035de78552d22dMarc Blank                    new String[] {Long.toString(mailbox.mId)});
1145c523858385176c33a7456bb84035de78552d22dMarc Blank            return;
1155c523858385176c33a7456bb84035de78552d22dMarc Blank        }
116560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy        LogUtils.d(TAG, "Mailbox: " + mailbox.mDisplayName);
1175c523858385176c33a7456bb84035de78552d22dMarc Blank
1185c523858385176c33a7456bb84035de78552d22dMarc Blank        Uri mailboxUri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId);
1195c523858385176c33a7456bb84035de78552d22dMarc Blank        ContentValues values = new ContentValues();
1205c523858385176c33a7456bb84035de78552d22dMarc Blank        // Set mailbox sync state
1215c523858385176c33a7456bb84035de78552d22dMarc Blank        values.put(Mailbox.UI_SYNC_STATUS,
1225c523858385176c33a7456bb84035de78552d22dMarc Blank                uiRefresh ? EmailContent.SYNC_STATUS_USER : EmailContent.SYNC_STATUS_BACKGROUND);
1235c523858385176c33a7456bb84035de78552d22dMarc Blank        resolver.update(mailboxUri, values, null, null);
1245c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
1255c523858385176c33a7456bb84035de78552d22dMarc Blank            try {
1265c523858385176c33a7456bb84035de78552d22dMarc Blank                String legacyImapProtocol = context.getString(R.string.protocol_legacy_imap);
1275c523858385176c33a7456bb84035de78552d22dMarc Blank                if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
1285c523858385176c33a7456bb84035de78552d22dMarc Blank                    EmailServiceStub.sendMailImpl(context, account.mId);
1295c523858385176c33a7456bb84035de78552d22dMarc Blank                } else if (protocol.equals(legacyImapProtocol)) {
13017d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                    ImapService.synchronizeMailboxSynchronous(context, account, mailbox,
13117d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                            deltaMessageCount);
1325c523858385176c33a7456bb84035de78552d22dMarc Blank                } else {
13317d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                    Pop3Service.synchronizeMailboxSynchronous(context, account, mailbox,
13417d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                            deltaMessageCount);
1355c523858385176c33a7456bb84035de78552d22dMarc Blank                }
1365c523858385176c33a7456bb84035de78552d22dMarc Blank            } catch (MessagingException e) {
1375c523858385176c33a7456bb84035de78552d22dMarc Blank                int cause = e.getExceptionType();
1385c523858385176c33a7456bb84035de78552d22dMarc Blank                switch(cause) {
1395c523858385176c33a7456bb84035de78552d22dMarc Blank                    case MessagingException.IOERROR:
1405c523858385176c33a7456bb84035de78552d22dMarc Blank                        syncResult.stats.numIoExceptions++;
1415c523858385176c33a7456bb84035de78552d22dMarc Blank                        break;
1425c523858385176c33a7456bb84035de78552d22dMarc Blank                    case MessagingException.AUTHENTICATION_FAILED:
1435c523858385176c33a7456bb84035de78552d22dMarc Blank                        syncResult.stats.numAuthExceptions++;
1445c523858385176c33a7456bb84035de78552d22dMarc Blank                        break;
1455c523858385176c33a7456bb84035de78552d22dMarc Blank                }
1465c523858385176c33a7456bb84035de78552d22dMarc Blank            }
1475c523858385176c33a7456bb84035de78552d22dMarc Blank        } finally {
14817d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu            // Always clear our sync state and update sync time.
1495c523858385176c33a7456bb84035de78552d22dMarc Blank            values.put(Mailbox.UI_SYNC_STATUS, EmailContent.SYNC_STATUS_NONE);
15017d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu            values.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
1515c523858385176c33a7456bb84035de78552d22dMarc Blank            resolver.update(mailboxUri, values, null, null);
1525c523858385176c33a7456bb84035de78552d22dMarc Blank        }
1535c523858385176c33a7456bb84035de78552d22dMarc Blank    }
1545c523858385176c33a7456bb84035de78552d22dMarc Blank
1555c523858385176c33a7456bb84035de78552d22dMarc Blank    /**
1565c523858385176c33a7456bb84035de78552d22dMarc Blank     * Partial integration with system SyncManager; we initiate manual syncs upon request
1575c523858385176c33a7456bb84035de78552d22dMarc Blank     */
1585c523858385176c33a7456bb84035de78552d22dMarc Blank    private static void performSync(Context context, android.accounts.Account account,
1591b8e0fa23f6e9957f0b8753dd3f5b95d3f5d98eaScott Kennedy            Bundle extras, ContentProviderClient provider, SyncResult syncResult) {
1605c523858385176c33a7456bb84035de78552d22dMarc Blank        // Find an EmailProvider account with the Account's email address
1615c523858385176c33a7456bb84035de78552d22dMarc Blank        Cursor c = null;
1625c523858385176c33a7456bb84035de78552d22dMarc Blank        try {
1635c523858385176c33a7456bb84035de78552d22dMarc Blank            c = provider.query(com.android.emailcommon.provider.Account.CONTENT_URI,
1645c523858385176c33a7456bb84035de78552d22dMarc Blank                    Account.CONTENT_PROJECTION, AccountColumns.EMAIL_ADDRESS + "=?",
1655c523858385176c33a7456bb84035de78552d22dMarc Blank                    new String[] {account.name}, null);
1665c523858385176c33a7456bb84035de78552d22dMarc Blank            if (c != null && c.moveToNext()) {
1675c523858385176c33a7456bb84035de78552d22dMarc Blank                Account acct = new Account();
1685c523858385176c33a7456bb84035de78552d22dMarc Blank                acct.restore(c);
1695c523858385176c33a7456bb84035de78552d22dMarc Blank                if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
170560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                    LogUtils.d(TAG, "Upload sync request for " + acct.mDisplayName);
1715c523858385176c33a7456bb84035de78552d22dMarc Blank                    // See if any boxes have mail...
1725c523858385176c33a7456bb84035de78552d22dMarc Blank                    ArrayList<Long> mailboxesToUpdate;
1735c523858385176c33a7456bb84035de78552d22dMarc Blank                    Cursor updatesCursor = provider.query(Message.UPDATED_CONTENT_URI,
1745c523858385176c33a7456bb84035de78552d22dMarc Blank                            new String[] {Message.MAILBOX_KEY},
1755c523858385176c33a7456bb84035de78552d22dMarc Blank                            Message.ACCOUNT_KEY + "=?",
1765c523858385176c33a7456bb84035de78552d22dMarc Blank                            new String[] {Long.toString(acct.mId)},
1775c523858385176c33a7456bb84035de78552d22dMarc Blank                            null);
1785c523858385176c33a7456bb84035de78552d22dMarc Blank                    try {
1795c523858385176c33a7456bb84035de78552d22dMarc Blank                        if ((updatesCursor == null) || (updatesCursor.getCount() == 0)) return;
1805c523858385176c33a7456bb84035de78552d22dMarc Blank                        mailboxesToUpdate = new ArrayList<Long>();
1815c523858385176c33a7456bb84035de78552d22dMarc Blank                        while (updatesCursor.moveToNext()) {
1825c523858385176c33a7456bb84035de78552d22dMarc Blank                            Long mailboxId = updatesCursor.getLong(0);
1835c523858385176c33a7456bb84035de78552d22dMarc Blank                            if (!mailboxesToUpdate.contains(mailboxId)) {
1845c523858385176c33a7456bb84035de78552d22dMarc Blank                                mailboxesToUpdate.add(mailboxId);
1855c523858385176c33a7456bb84035de78552d22dMarc Blank                            }
1865c523858385176c33a7456bb84035de78552d22dMarc Blank                        }
1875c523858385176c33a7456bb84035de78552d22dMarc Blank                    } finally {
1885c523858385176c33a7456bb84035de78552d22dMarc Blank                        if (updatesCursor != null) {
1895c523858385176c33a7456bb84035de78552d22dMarc Blank                            updatesCursor.close();
1905c523858385176c33a7456bb84035de78552d22dMarc Blank                        }
1915c523858385176c33a7456bb84035de78552d22dMarc Blank                    }
1925c523858385176c33a7456bb84035de78552d22dMarc Blank                    for (long mailboxId: mailboxesToUpdate) {
19317d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                        sync(context, mailboxId, syncResult, false, 0);
1945c523858385176c33a7456bb84035de78552d22dMarc Blank                    }
1955c523858385176c33a7456bb84035de78552d22dMarc Blank                } else {
196560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                    LogUtils.d(TAG, "Sync request for " + acct.mDisplayName);
197560bfadc3151f7a06f3b06e9a6c92cfa534c63ecScott Kennedy                    LogUtils.d(TAG, extras.toString());
1989e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                    long mailboxId =
1999e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                            extras.getLong(Mailbox.SYNC_EXTRA_MAILBOX_ID, Mailbox.NO_MAILBOX);
2005c523858385176c33a7456bb84035de78552d22dMarc Blank                    if (mailboxId == Mailbox.NO_MAILBOX) {
20122130ad565886c29666be55b5fdcbe0be9894b6eYu Ping Hu                        // Update folders.
20222130ad565886c29666be55b5fdcbe0be9894b6eYu Ping Hu                        EmailServiceProxy service =
20322130ad565886c29666be55b5fdcbe0be9894b6eYu Ping Hu                                EmailServiceUtils.getServiceForAccount(context, null, acct.mId);
20422130ad565886c29666be55b5fdcbe0be9894b6eYu Ping Hu                        service.updateFolderList(acct.mId);
2055c523858385176c33a7456bb84035de78552d22dMarc Blank                        mailboxId = Mailbox.findMailboxOfType(context, acct.mId,
2065c523858385176c33a7456bb84035de78552d22dMarc Blank                                Mailbox.TYPE_INBOX);
2075c523858385176c33a7456bb84035de78552d22dMarc Blank                    }
2085c523858385176c33a7456bb84035de78552d22dMarc Blank                    if (mailboxId == Mailbox.NO_MAILBOX) return;
2095c523858385176c33a7456bb84035de78552d22dMarc Blank                    boolean uiRefresh =
2105c523858385176c33a7456bb84035de78552d22dMarc Blank                            extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
21117d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                    int deltaMessageCount =
2129e7f5a2a33a31ff392d3116f6432b2f93ffe8e71Yu Ping Hu                            extras.getInt(Mailbox.SYNC_EXTRA_DELTA_MESSAGE_COUNT, 0);
21317d5bbf768c27ac7782b155e2ab25bcd480f5dcfYu Ping Hu                    sync(context, mailboxId, syncResult, uiRefresh, deltaMessageCount);
2145c523858385176c33a7456bb84035de78552d22dMarc Blank                }
2155c523858385176c33a7456bb84035de78552d22dMarc Blank            }
2165c523858385176c33a7456bb84035de78552d22dMarc Blank        } catch (Exception e) {
2175c523858385176c33a7456bb84035de78552d22dMarc Blank            e.printStackTrace();
2185c523858385176c33a7456bb84035de78552d22dMarc Blank        } finally {
2195c523858385176c33a7456bb84035de78552d22dMarc Blank            if (c != null) {
2205c523858385176c33a7456bb84035de78552d22dMarc Blank                c.close();
2215c523858385176c33a7456bb84035de78552d22dMarc Blank            }
2225c523858385176c33a7456bb84035de78552d22dMarc Blank        }
2235c523858385176c33a7456bb84035de78552d22dMarc Blank    }
2245c523858385176c33a7456bb84035de78552d22dMarc Blank}