• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.app.Service;
20 import android.content.AbstractThreadedSyncAdapter;
21 import android.content.ContentProviderClient;
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.SyncResult;
28 import android.database.Cursor;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.IBinder;
32 
33 import com.android.email.R;
34 import com.android.emailcommon.TempDirectory;
35 import com.android.emailcommon.mail.MessagingException;
36 import com.android.emailcommon.provider.Account;
37 import com.android.emailcommon.provider.EmailContent;
38 import com.android.emailcommon.provider.EmailContent.AccountColumns;
39 import com.android.emailcommon.provider.EmailContent.Message;
40 import com.android.emailcommon.provider.Mailbox;
41 import com.android.emailcommon.service.EmailServiceProxy;
42 import com.android.emailcommon.service.EmailServiceStatus;
43 import com.android.mail.providers.UIProvider;
44 import com.android.mail.utils.LogUtils;
45 
46 import java.util.ArrayList;
47 
48 public class PopImapSyncAdapterService extends Service {
49     private static final String TAG = "PopImapSyncService";
50     private SyncAdapterImpl mSyncAdapter = null;
51 
PopImapSyncAdapterService()52     public PopImapSyncAdapterService() {
53         super();
54     }
55 
56     private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
SyncAdapterImpl(Context context)57         public SyncAdapterImpl(Context context) {
58             super(context, true /* autoInitialize */);
59         }
60 
61         @Override
onPerformSync(android.accounts.Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult)62         public void onPerformSync(android.accounts.Account account, Bundle extras,
63                 String authority, ContentProviderClient provider, SyncResult syncResult) {
64             PopImapSyncAdapterService.performSync(getContext(), account, extras, provider,
65                     syncResult);
66         }
67     }
68 
69     @Override
onCreate()70     public void onCreate() {
71         super.onCreate();
72         mSyncAdapter = new SyncAdapterImpl(getApplicationContext());
73     }
74 
75     @Override
onBind(Intent intent)76     public IBinder onBind(Intent intent) {
77         return mSyncAdapter.getSyncAdapterBinder();
78     }
79 
80     /**
81      * @return whether or not this mailbox retrieves its data from the server (as opposed to just
82      *     a local mailbox that is never synced).
83      */
loadsFromServer(Context context, Mailbox m, String protocol)84     private static boolean loadsFromServer(Context context, Mailbox m, String protocol) {
85         String legacyImapProtocol = context.getString(R.string.protocol_legacy_imap);
86         String pop3Protocol = context.getString(R.string.protocol_pop3);
87         if (legacyImapProtocol.equals(protocol)) {
88             // TODO: actually use a sync flag when creating the mailboxes. Right now we use an
89             // approximation for IMAP.
90             return m.mType != Mailbox.TYPE_DRAFTS
91                     && m.mType != Mailbox.TYPE_OUTBOX
92                     && m.mType != Mailbox.TYPE_SEARCH;
93 
94         } else if (pop3Protocol.equals(protocol)) {
95             return Mailbox.TYPE_INBOX == m.mType;
96         }
97 
98         return false;
99     }
100 
sync(final Context context, final long mailboxId, final Bundle extras, final SyncResult syncResult, final boolean uiRefresh, final int deltaMessageCount)101     private static void sync(final Context context, final long mailboxId,
102             final Bundle extras, final SyncResult syncResult, final boolean uiRefresh,
103             final int deltaMessageCount) {
104         TempDirectory.setTempDirectory(context);
105         Mailbox mailbox = Mailbox.restoreMailboxWithId(context, mailboxId);
106         if (mailbox == null) return;
107         Account account = Account.restoreAccountWithId(context, mailbox.mAccountKey);
108         if (account == null) return;
109         ContentResolver resolver = context.getContentResolver();
110         String protocol = account.getProtocol(context);
111         if ((mailbox.mType != Mailbox.TYPE_OUTBOX) &&
112                 !loadsFromServer(context, mailbox, protocol)) {
113             // This is an update to a message in a non-syncing mailbox; delete this from the
114             // updates table and return
115             resolver.delete(Message.UPDATED_CONTENT_URI, Message.MAILBOX_KEY + "=?",
116                     new String[] {Long.toString(mailbox.mId)});
117             return;
118         }
119         LogUtils.d(TAG, "About to sync mailbox: " + mailbox.mDisplayName);
120 
121         Uri mailboxUri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId);
122         ContentValues values = new ContentValues();
123         // Set mailbox sync state
124         values.put(Mailbox.UI_SYNC_STATUS,
125                 uiRefresh ? EmailContent.SYNC_STATUS_USER : EmailContent.SYNC_STATUS_BACKGROUND);
126         resolver.update(mailboxUri, values, null, null);
127         try {
128             try {
129                 String legacyImapProtocol = context.getString(R.string.protocol_legacy_imap);
130                 if (mailbox.mType == Mailbox.TYPE_OUTBOX) {
131                     EmailServiceStub.sendMailImpl(context, account.mId);
132                 } else {
133                     EmailServiceStatus.syncMailboxStatus(resolver, extras, mailboxId,
134                             EmailServiceStatus.IN_PROGRESS, 0, UIProvider.LastSyncResult.SUCCESS);
135                     final int status;
136                     if (protocol.equals(legacyImapProtocol)) {
137                         status = ImapService.synchronizeMailboxSynchronous(context, account,
138                                 mailbox, deltaMessageCount != 0, uiRefresh);
139                     } else {
140                         status = Pop3Service.synchronizeMailboxSynchronous(context, account,
141                                 mailbox, deltaMessageCount);
142                     }
143                     EmailServiceStatus.syncMailboxStatus(resolver, extras, mailboxId, status, 0,
144                             UIProvider.LastSyncResult.SUCCESS);
145                 }
146             } catch (MessagingException e) {
147                 int cause = e.getExceptionType();
148                 // XXX It's no good to put the MessagingException.cause here, that's not the
149                 // same set of values that we use in EmailServiceStatus.
150                 switch(cause) {
151                     case MessagingException.IOERROR:
152                         EmailServiceStatus.syncMailboxStatus(resolver, extras, mailboxId, cause, 0,
153                                 UIProvider.LastSyncResult.CONNECTION_ERROR);
154                         syncResult.stats.numIoExceptions++;
155                         break;
156                     case MessagingException.AUTHENTICATION_FAILED:
157                         EmailServiceStatus.syncMailboxStatus(resolver, extras, mailboxId, cause, 0,
158                                 UIProvider.LastSyncResult.AUTH_ERROR);
159                         syncResult.stats.numAuthExceptions++;
160                         break;
161 
162                     default:
163                     EmailServiceStatus.syncMailboxStatus(resolver, extras, mailboxId, cause, 0,
164                             UIProvider.LastSyncResult.INTERNAL_ERROR);
165                 }
166             }
167         } finally {
168             // Always clear our sync state and update sync time.
169             values.put(Mailbox.UI_SYNC_STATUS, EmailContent.SYNC_STATUS_NONE);
170             values.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
171             resolver.update(mailboxUri, values, null, null);
172         }
173     }
174 
175     /**
176      * Partial integration with system SyncManager; we initiate manual syncs upon request
177      */
performSync(Context context, android.accounts.Account account, Bundle extras, ContentProviderClient provider, SyncResult syncResult)178     private static void performSync(Context context, android.accounts.Account account,
179             Bundle extras, ContentProviderClient provider, SyncResult syncResult) {
180         // Find an EmailProvider account with the Account's email address
181         Cursor c = null;
182         try {
183             c = provider.query(com.android.emailcommon.provider.Account.CONTENT_URI,
184                     Account.CONTENT_PROJECTION, AccountColumns.EMAIL_ADDRESS + "=?",
185                     new String[] {account.name}, null);
186             if (c != null && c.moveToNext()) {
187                 Account acct = new Account();
188                 acct.restore(c);
189                 if (extras.getBoolean(ContentResolver.SYNC_EXTRAS_UPLOAD)) {
190                     LogUtils.d(TAG, "Upload sync request for " + acct.mDisplayName);
191                     // See if any boxes have mail...
192                     ArrayList<Long> mailboxesToUpdate;
193                     Cursor updatesCursor = provider.query(Message.UPDATED_CONTENT_URI,
194                             new String[] {Message.MAILBOX_KEY},
195                             Message.ACCOUNT_KEY + "=?",
196                             new String[] {Long.toString(acct.mId)},
197                             null);
198                     try {
199                         if ((updatesCursor == null) || (updatesCursor.getCount() == 0)) return;
200                         mailboxesToUpdate = new ArrayList<Long>();
201                         while (updatesCursor.moveToNext()) {
202                             Long mailboxId = updatesCursor.getLong(0);
203                             if (!mailboxesToUpdate.contains(mailboxId)) {
204                                 mailboxesToUpdate.add(mailboxId);
205                             }
206                         }
207                     } finally {
208                         if (updatesCursor != null) {
209                             updatesCursor.close();
210                         }
211                     }
212                     for (long mailboxId: mailboxesToUpdate) {
213                         sync(context, mailboxId, extras, syncResult, false, 0);
214                     }
215                 } else {
216                     LogUtils.d(TAG, "Sync request for " + acct.mDisplayName);
217                     LogUtils.d(TAG, extras.toString());
218 
219                     // We update our folder structure on every sync.
220                     final EmailServiceProxy service =
221                             EmailServiceUtils.getServiceForAccount(context, acct.mId);
222                     service.updateFolderList(acct.mId);
223 
224                     // Get the id for the mailbox we want to sync.
225                     long [] mailboxIds = Mailbox.getMailboxIdsFromBundle(extras);
226                     if (mailboxIds == null || mailboxIds.length == 0) {
227                         // No mailbox specified, just sync the inbox.
228                         // TODO: IMAP may eventually want to allow multiple auto-sync mailboxes.
229                         final long inboxId = Mailbox.findMailboxOfType(context, acct.mId,
230                                 Mailbox.TYPE_INBOX);
231                         if (inboxId != Mailbox.NO_MAILBOX) {
232                             mailboxIds = new long[1];
233                             mailboxIds[0] = inboxId;
234                         }
235                     }
236 
237                     if (mailboxIds != null) {
238                         boolean uiRefresh =
239                             extras.getBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, false);
240                         int deltaMessageCount =
241                                 extras.getInt(Mailbox.SYNC_EXTRA_DELTA_MESSAGE_COUNT, 0);
242                         for (long mailboxId : mailboxIds) {
243                             sync(context, mailboxId, extras, syncResult, uiRefresh, deltaMessageCount);
244                         }
245                     }
246                 }
247             }
248         } catch (Exception e) {
249             e.printStackTrace();
250         } finally {
251             if (c != null) {
252                 c.close();
253             }
254         }
255     }
256 }
257