18a574694606f0e5d781334d0d426fc379c51f3edMarc Blank/* 28a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * Copyright (C) 2008 The Android Open Source Project 38a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * 48a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 58a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * you may not use this file except in compliance with the License. 68a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * You may obtain a copy of the License at 78a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * 88a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * http://www.apache.org/licenses/LICENSE-2.0 98a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * 108a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * Unless required by applicable law or agreed to in writing, software 118a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * distributed under the License is distributed on an "AS IS" BASIS, 128a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 138a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * See the License for the specific language governing permissions and 148a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * limitations under the License. 158a574694606f0e5d781334d0d426fc379c51f3edMarc Blank */ 168a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 178a574694606f0e5d781334d0d426fc379c51f3edMarc Blankpackage com.android.email.service; 188a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 198a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport android.accounts.AccountManager; 208a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport android.accounts.AccountManagerCallback; 21bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.app.AlarmManager; 22bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.app.PendingIntent; 238a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport android.app.Service; 24bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.ContentResolver; 25bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.ContentUris; 268a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport android.content.Context; 278a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport android.content.Intent; 28bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.content.SyncStatusObserver; 298a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport android.database.Cursor; 30bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.net.Uri; 318a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport android.os.Bundle; 328a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport android.os.IBinder; 33bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.os.SystemClock; 34bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport android.text.TextUtils; 35f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blankimport android.util.Log; 368a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 37bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Controller; 38bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Email; 39bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.Preferences; 40bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.email.SingleRunningTask; 414e4aba9ebc43c6a83190f3a883fa05bb7d5100b3Marc Blankimport com.android.email.provider.AccountReconciler; 42bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.AccountManagerTypes; 43bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.mail.MessagingException; 449863dac3ee1af2b5f81e77245410e21e5db5613fMarc Blankimport com.android.emailcommon.provider.Account; 45bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.EmailContent; 469863dac3ee1af2b5f81e77245410e21e5db5613fMarc Blankimport com.android.emailcommon.provider.HostAuth; 47bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport com.android.emailcommon.provider.Mailbox; 489863dac3ee1af2b5f81e77245410e21e5db5613fMarc Blankimport com.android.emailcommon.utility.EmailAsyncTask; 499863dac3ee1af2b5f81e77245410e21e5db5613fMarc Blankimport com.google.common.annotations.VisibleForTesting; 509863dac3ee1af2b5f81e77245410e21e5db5613fMarc Blank 518a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport java.util.ArrayList; 52bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrookimport java.util.HashMap; 538a574694606f0e5d781334d0d426fc379c51f3edMarc Blankimport java.util.List; 548a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 558a574694606f0e5d781334d0d426fc379c51f3edMarc Blank/** 56bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Background service for refreshing non-push email accounts. 57bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 58bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * TODO: Convert to IntentService to move *all* work off the UI thread, serialize work, and avoid 59bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * possible problems with out-of-order startId processing. 608a574694606f0e5d781334d0d426fc379c51f3edMarc Blank */ 618a574694606f0e5d781334d0d426fc379c51f3edMarc Blankpublic class MailService extends Service { 62bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String LOG_TAG = "Email-MailService"; 63bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 64bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String ACTION_CHECK_MAIL = 65bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook "com.android.email.intent.action.MAIL_SERVICE_WAKEUP"; 66bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String ACTION_RESCHEDULE = 67bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook "com.android.email.intent.action.MAIL_SERVICE_RESCHEDULE"; 68bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String ACTION_CANCEL = 69bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook "com.android.email.intent.action.MAIL_SERVICE_CANCEL"; 70bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String ACTION_SEND_PENDING_MAIL = 71bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook "com.android.email.intent.action.MAIL_SERVICE_SEND_PENDING"; 72bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String ACTION_DELETE_EXCHANGE_ACCOUNTS = 73bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook "com.android.email.intent.action.MAIL_SERVICE_DELETE_EXCHANGE_ACCOUNTS"; 74bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 75bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String EXTRA_ACCOUNT = "com.android.email.intent.extra.ACCOUNT"; 76bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String EXTRA_ACCOUNT_INFO = "com.android.email.intent.extra.ACCOUNT_INFO"; 77bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final String EXTRA_DEBUG_WATCHDOG = "com.android.email.intent.extra.WATCHDOG"; 78bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 79bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Time between watchdog checks; in milliseconds */ 80bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final long WATCHDOG_DELAY = 10 * 60 * 1000; // 10 minutes 81bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 82bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Sentinel value asking to update mSyncReports if it's currently empty */ 83bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @VisibleForTesting 84bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook static final int SYNC_REPORTS_ALL_ACCOUNTS_IF_EMPTY = -1; 85bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Sentinel value asking that mSyncReports be rebuilt */ 86bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @VisibleForTesting 87bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook static final int SYNC_REPORTS_RESET = -2; 88bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 89bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @VisibleForTesting 90bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Controller mController; 91bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private final Controller.Result mControllerCallback = new ControllerResults(); 92bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private ContentResolver mContentResolver; 93bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private Context mContext; 94bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 95bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private int mStartId; 96bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 97bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 98bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Access must be synchronized, because there are accesses from the Controller callback 99bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 100bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /*package*/ static HashMap<Long,AccountSyncReport> mSyncReports = 101bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook new HashMap<Long,AccountSyncReport>(); 102bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 103bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static void actionReschedule(Context context) { 104bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Intent i = new Intent(); 105bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setClass(context, MailService.class); 106bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setAction(MailService.ACTION_RESCHEDULE); 107bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook context.startService(i); 108bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 109bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 110bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static void actionCancel(Context context) { 111bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Intent i = new Intent(); 112bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setClass(context, MailService.class); 113bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setAction(MailService.ACTION_CANCEL); 114bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook context.startService(i); 115bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 116bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 117bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static void actionDeleteExchangeAccounts(Context context) { 118bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Intent i = new Intent(); 119bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setClass(context, MailService.class); 120bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setAction(MailService.ACTION_DELETE_EXCHANGE_ACCOUNTS); 121bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook context.startService(i); 122bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 123bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 124bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 125bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Entry point for AttachmentDownloadService to ask that pending mail be sent 126bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param context the caller's context 127bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param accountId the account whose pending mail should be sent 128bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 129bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static void actionSendPendingMail(Context context, long accountId) { 130bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Intent i = new Intent(); 131bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setClass(context, MailService.class); 132bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setAction(MailService.ACTION_SEND_PENDING_MAIL); 133bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.putExtra(MailService.EXTRA_ACCOUNT, accountId); 134bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook context.startService(i); 135bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 1368a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 1378a574694606f0e5d781334d0d426fc379c51f3edMarc Blank @Override 1388a574694606f0e5d781334d0d426fc379c51f3edMarc Blank public int onStartCommand(final Intent intent, int flags, final int startId) { 1398a574694606f0e5d781334d0d426fc379c51f3edMarc Blank super.onStartCommand(intent, flags, startId); 140bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 141bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook EmailAsyncTask.runAsyncParallel(new Runnable() { 142bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 143bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void run() { 144bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook reconcilePopImapAccountsSync(MailService.this); 145bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 146bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook }); 147bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 148bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // TODO this needs to be passed through the controller and back to us 149bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mStartId = startId; 150bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String action = intent.getAction(); 151bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook final long accountId = intent.getLongExtra(EXTRA_ACCOUNT, -1); 152bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 153bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mController = Controller.getInstance(this); 154bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mController.addResultCallback(mControllerCallback); 155bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mContentResolver = getContentResolver(); 156bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mContext = this; 157bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 158bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); 159bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 160bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (ACTION_CHECK_MAIL.equals(action)) { 161bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // DB access required to satisfy this intent, so offload from UI thread 162bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook EmailAsyncTask.runAsyncParallel(new Runnable() { 163bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 164bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void run() { 165bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // If we have the data, restore the last-sync-times for each account 166bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // These are cached in the wakeup intent in case the process was killed. 167bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook restoreSyncReports(intent); 168bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 169bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Sync a specific account if given 170bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 171bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "action: check mail for id=" + accountId); 172bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 173bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (accountId >= 0) { 174bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook setWatchdog(accountId, alarmManager); 175bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 176bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 177bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Start sync if account is given && auto-sync is allowed 178bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook boolean syncStarted = false; 179bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (accountId != -1 && ContentResolver.getMasterSyncAutomatically()) { 180bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook synchronized(mSyncReports) { 181bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook for (AccountSyncReport report: mSyncReports.values()) { 182bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (report.accountId == accountId) { 183bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (report.syncEnabled) { 184bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook syncStarted = syncOneAccount(mController, accountId, 185bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook startId); 186bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 187bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook break; 188bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 189bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 190bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 191bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 192bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 193bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Reschedule if we didn't start sync. 194bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (!syncStarted) { 195bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Prevent runaway on the current account by pretending it updated 196bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (accountId != -1) { 197bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook updateAccountReport(accountId, 0); 198bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 199bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Find next account to sync, and reschedule 200bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook reschedule(alarmManager); 201bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Stop the service, unless actually syncing (which will stop the service) 202bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook stopSelf(startId); 203bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 204bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 205bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook }); 206bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 207bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook else if (ACTION_CANCEL.equals(action)) { 208bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 209bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "action: cancel"); 210bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 211bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook cancel(); 212bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook stopSelf(startId); 213bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 214bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook else if (ACTION_DELETE_EXCHANGE_ACCOUNTS.equals(action)) { 215bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 216bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "action: delete exchange accounts"); 217bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 218bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook EmailAsyncTask.runAsyncParallel(new Runnable() { 219bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 220bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void run() { 221bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Cursor c = mContentResolver.query(Account.CONTENT_URI, Account.ID_PROJECTION, 222bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook null, null, null); 223bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook try { 224bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook while (c.moveToNext()) { 225bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long accountId = c.getLong(Account.ID_PROJECTION_COLUMN); 226bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if ("eas".equals(Account.getProtocol(mContext, accountId))) { 227bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Always log this 228bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "Deleting EAS account: " + accountId); 229bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mController.deleteAccountSync(accountId, mContext); 230bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 231bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 232bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } finally { 233bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook c.close(); 234bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 235bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 236bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook }); 237bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook stopSelf(startId); 238bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 239bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook else if (ACTION_SEND_PENDING_MAIL.equals(action)) { 240bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 241bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "action: send pending mail"); 242bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 243bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook EmailAsyncTask.runAsyncParallel(new Runnable() { 244bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 245bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void run() { 246bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mController.sendPendingMessages(accountId); 247bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 248bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook }); 249bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook stopSelf(startId); 250bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 251bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook else if (ACTION_RESCHEDULE.equals(action)) { 252bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 253bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "action: reschedule"); 254bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 255bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // DB access required to satisfy this intent, so offload from UI thread 256bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook EmailAsyncTask.runAsyncParallel(new Runnable() { 257bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 258bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void run() { 259bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // When called externally, we refresh the sync reports table to pick up 260bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // any changes in the account list or account settings 261bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook refreshSyncReports(); 262bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Finally, scan for the next needing update, and set an alarm for it 263bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook reschedule(alarmManager); 264bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook stopSelf(startId); 265bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 266bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook }); 267bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 268bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 269bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Returning START_NOT_STICKY means that if a mail check is killed (e.g. due to memory 270bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // pressure, there will be no explicit restart. This is OK; Note that we set a watchdog 271bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // alarm before each mailbox check. If the mailbox check never completes, the watchdog 272bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // will fire and get things running again. 273bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return START_NOT_STICKY; 2748a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } 2758a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 2768a574694606f0e5d781334d0d426fc379c51f3edMarc Blank @Override 2778a574694606f0e5d781334d0d426fc379c51f3edMarc Blank public IBinder onBind(Intent intent) { 2788a574694606f0e5d781334d0d426fc379c51f3edMarc Blank return null; 2798a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } 2808a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 281bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 282bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void onDestroy() { 283bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook super.onDestroy(); 284bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Controller.getInstance(getApplication()).removeResultCallback(mControllerCallback); 285bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 286bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 287bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private void cancel() { 288bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AlarmManager alarmMgr = (AlarmManager)getSystemService(Context.ALARM_SERVICE); 289bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook PendingIntent pi = createAlarmIntent(-1, null, false); 290bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook alarmMgr.cancel(pi); 291bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 292bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 293bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 294bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Refresh the sync reports, to pick up any changes in the account list or account settings. 295bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 296bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private void refreshSyncReports() { 297bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook synchronized (mSyncReports) { 298bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Make shallow copy of sync reports so we can recover the prev sync times 299bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook HashMap<Long,AccountSyncReport> oldSyncReports = 300bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook new HashMap<Long,AccountSyncReport>(mSyncReports); 301bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 302bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Delete the sync reports to force a refresh from live account db data 303bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook setupSyncReportsLocked(SYNC_REPORTS_RESET, this); 304bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 305bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Restore prev-sync & next-sync times for any reports in the new list 306bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook for (AccountSyncReport newReport : mSyncReports.values()) { 307bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountSyncReport oldReport = oldSyncReports.get(newReport.accountId); 308bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (oldReport != null) { 309bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook newReport.prevSyncTime = oldReport.prevSyncTime; 310bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook newReport.setNextSyncTime(); 311bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 312bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 313bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 314bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 315bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 316bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 317bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Create and send an alarm with the entire list. This also sends a list of known last-sync 318bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * times with the alarm, so if we are killed between alarms, we don't lose this info. 319bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 320bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param alarmMgr passed in so we can mock for testing. 321bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 322bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private void reschedule(AlarmManager alarmMgr) { 323bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // restore the reports if lost 324bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook setupSyncReports(SYNC_REPORTS_ALL_ACCOUNTS_IF_EMPTY); 325bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook synchronized (mSyncReports) { 326bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int numAccounts = mSyncReports.size(); 327bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long[] accountInfo = new long[numAccounts * 2]; // pairs of { accountId, lastSync } 328bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int accountInfoIndex = 0; 329bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 330bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long nextCheckTime = Long.MAX_VALUE; 331bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountSyncReport nextAccount = null; 332bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long timeNow = SystemClock.elapsedRealtime(); 333bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 334bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook for (AccountSyncReport report : mSyncReports.values()) { 335bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (report.syncInterval <= 0) { // no timed checks - skip 336bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook continue; 337bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 338bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long prevSyncTime = report.prevSyncTime; 339bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long nextSyncTime = report.nextSyncTime; 340bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 341bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // select next account to sync 342bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if ((prevSyncTime == 0) || (nextSyncTime < timeNow)) { // never checked, or overdue 343bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook nextCheckTime = 0; 344bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook nextAccount = report; 345bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else if (nextSyncTime < nextCheckTime) { // next to be checked 346bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook nextCheckTime = nextSyncTime; 347bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook nextAccount = report; 348bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 349bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // collect last-sync-times for all accounts 350bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // this is using pairs of {long,long} to simplify passing in a bundle 351bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook accountInfo[accountInfoIndex++] = report.accountId; 352bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook accountInfo[accountInfoIndex++] = report.prevSyncTime; 353bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 354bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 355bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Clear out any unused elements in the array 356bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook while (accountInfoIndex < accountInfo.length) { 357bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook accountInfo[accountInfoIndex++] = -1; 358bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 359bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 360bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // set/clear alarm as needed 361bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long idToCheck = (nextAccount == null) ? -1 : nextAccount.accountId; 362bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook PendingIntent pi = createAlarmIntent(idToCheck, accountInfo, false); 363bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 364bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (nextAccount == null) { 365bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook alarmMgr.cancel(pi); 366bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 367bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "reschedule: alarm cancel - no account to check"); 368bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 369bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 370bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextCheckTime, pi); 371bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 372bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "reschedule: alarm set at " + nextCheckTime 373bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook + " for " + nextAccount); 374bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 375bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 376bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 377bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 378bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 379bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 380bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Create a watchdog alarm and set it. This is used in case a mail check fails (e.g. we are 381bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * killed by the system due to memory pressure.) Normally, a mail check will complete and 382bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * the watchdog will be replaced by the call to reschedule(). 383bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param accountId the account we were trying to check 384bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param alarmMgr system alarm manager 385bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 386bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private void setWatchdog(long accountId, AlarmManager alarmMgr) { 387bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook PendingIntent pi = createAlarmIntent(accountId, null, true); 388bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long timeNow = SystemClock.elapsedRealtime(); 389bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long nextCheckTime = timeNow + WATCHDOG_DELAY; 390bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook alarmMgr.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, nextCheckTime, pi); 391bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 392bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 393bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 394bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Return a pending intent for use by this alarm. Most of the fields must be the same 395bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * (in order for the intent to be recognized by the alarm manager) but the extras can 396bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * be different, and are passed in here as parameters. 397bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 398bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private PendingIntent createAlarmIntent(long checkId, long[] accountInfo, boolean isWatchdog) { 399bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Intent i = new Intent(); 400bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setClass(this, MailService.class); 401bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.setAction(ACTION_CHECK_MAIL); 402bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.putExtra(EXTRA_ACCOUNT, checkId); 403bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.putExtra(EXTRA_ACCOUNT_INFO, accountInfo); 404bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (isWatchdog) { 405bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook i.putExtra(EXTRA_DEBUG_WATCHDOG, true); 406bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 407bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook PendingIntent pi = PendingIntent.getService(this, 0, i, PendingIntent.FLAG_UPDATE_CURRENT); 408bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return pi; 409bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 410bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 411bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 412bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Start a controller sync for a specific account 413bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 414bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param controller The controller to do the sync work 415bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param checkAccountId the account Id to try and check 416bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param startId the id of this service launch 417bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @return true if mail checking has started, false if it could not (e.g. bad account id) 418bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 419bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private boolean syncOneAccount(Controller controller, long checkAccountId, int startId) { 420bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long inboxId = Mailbox.findMailboxOfType(this, checkAccountId, Mailbox.TYPE_INBOX); 421bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (inboxId == Mailbox.NO_MAILBOX) { 422bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return false; 423bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 424bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook controller.serviceCheckMail(checkAccountId, inboxId, startId); 425bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return true; 426bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 427bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 428bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 429bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 430bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Note: Times are relative to SystemClock.elapsedRealtime() 431bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 432bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * TODO: Look more closely at syncEnabled and see if we can simply coalesce it into 433bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * syncInterval (e.g. if !syncEnabled, set syncInterval to -1). 434bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 435bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @VisibleForTesting 436bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook static class AccountSyncReport { 437bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long accountId; 438bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** The time of the last sync, or, {@code 0}, the last sync time is unknown. */ 439bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long prevSyncTime; 440bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** The time of the next sync. If {@code 0}, sync ASAP. If {@code 1}, don't sync. */ 441bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long nextSyncTime; 442bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** Minimum time between syncs; in minutes. */ 443bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int syncInterval; 444bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** If {@code true}, auto sync is enabled. */ 445bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook boolean syncEnabled; 446bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 447bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 448bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Sets the next sync time using the previous sync time and sync interval. 449bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 450bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private void setNextSyncTime() { 451bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (syncInterval > 0 && prevSyncTime != 0) { 452bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook nextSyncTime = prevSyncTime + (syncInterval * 1000 * 60); 453bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 454bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 455bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 456bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 457bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public String toString() { 458bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return "id=" + accountId + " prevSync=" + prevSyncTime + " nextSync=" + nextSyncTime; 459bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 460bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 461bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 462bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 463bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * scan accounts to create a list of { acct, prev sync, next sync, #new } 464bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * use this to create a fresh copy. assumes all accounts need sync 465bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 466bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param accountId -1 will rebuild the list if empty. other values will force loading 467bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * of a single account (e.g if it was created after the original list population) 468bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 469bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private void setupSyncReports(long accountId) { 470bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook synchronized (mSyncReports) { 471bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook setupSyncReportsLocked(accountId, mContext); 472bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 473bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 474bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 475bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 476bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Handle the work of setupSyncReports. Must be synchronized on mSyncReports. 477bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 478bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @VisibleForTesting 479bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook void setupSyncReportsLocked(long accountId, Context context) { 480bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ContentResolver resolver = context.getContentResolver(); 481bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (accountId == SYNC_REPORTS_RESET) { 482bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // For test purposes, force refresh of mSyncReports 483bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mSyncReports.clear(); 484bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook accountId = SYNC_REPORTS_ALL_ACCOUNTS_IF_EMPTY; 485bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else if (accountId == SYNC_REPORTS_ALL_ACCOUNTS_IF_EMPTY) { 486bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // -1 == reload the list if empty, otherwise exit immediately 487bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (mSyncReports.size() > 0) { 488bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return; 489bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 490bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 491bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // load a single account if it doesn't already have a sync record 492bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (mSyncReports.containsKey(accountId)) { 493bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return; 494bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 495bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 496bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 497bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // setup to add a single account or all accounts 498bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Uri uri; 499bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (accountId == SYNC_REPORTS_ALL_ACCOUNTS_IF_EMPTY) { 500bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook uri = Account.CONTENT_URI; 501bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 502bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook uri = ContentUris.withAppendedId(Account.CONTENT_URI, accountId); 503bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 504bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 505bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook final boolean oneMinuteRefresh 506bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook = Preferences.getPreferences(this).getForceOneMinuteRefresh(); 507bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (oneMinuteRefresh) { 508bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.w(LOG_TAG, "One-minute refresh enabled."); 509bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 510bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 511bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // We use a full projection here because we'll restore each account object from it 512bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Cursor c = resolver.query(uri, Account.CONTENT_PROJECTION, null, null, null); 5138a574694606f0e5d781334d0d426fc379c51f3edMarc Blank try { 5148a574694606f0e5d781334d0d426fc379c51f3edMarc Blank while (c.moveToNext()) { 515bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Account account = Account.getContent(c, Account.class); 516bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // The following sanity checks are primarily for the sake of ignoring non-user 517bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // accounts that may have been left behind e.g. by failed unit tests. 518bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // Properly-formed accounts will always pass these simple checks. 519bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (TextUtils.isEmpty(account.mEmailAddress) 520bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook || account.mHostAuthKeyRecv <= 0 521bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook || account.mHostAuthKeySend <= 0) { 522bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook continue; 523bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 524bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 525bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // The account is OK, so proceed 526bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountSyncReport report = new AccountSyncReport(); 527bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int syncInterval = account.mSyncInterval; 528bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 529bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // If we're not using MessagingController (EAS at this point), don't schedule syncs 530bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (!mController.isMessagingController(account.mId)) { 531bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook syncInterval = Account.CHECK_INTERVAL_NEVER; 532bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else if (oneMinuteRefresh && syncInterval >= 0) { 533bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook syncInterval = 1; 5348a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } 535bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 536bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.accountId = account.mId; 537bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.prevSyncTime = 0; 538bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.nextSyncTime = (syncInterval > 0) ? 0 : -1; // 0 == ASAP -1 == no sync 539bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 540bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.syncInterval = syncInterval; 541bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 542bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // See if the account is enabled for sync in AccountManager 543bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook android.accounts.Account accountManagerAccount = 544bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook new android.accounts.Account(account.mEmailAddress, 545bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountManagerTypes.TYPE_POP_IMAP); 546bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.syncEnabled = ContentResolver.getSyncAutomatically(accountManagerAccount, 547bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook EmailContent.AUTHORITY); 548bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 549bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // TODO lookup # new in inbox 550bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook mSyncReports.put(report.accountId, report); 5518a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } 5528a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } finally { 5538a574694606f0e5d781334d0d426fc379c51f3edMarc Blank c.close(); 5548a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } 5558a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } 5568a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 5578a574694606f0e5d781334d0d426fc379c51f3edMarc Blank /** 558bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Update list with a single account's sync times and unread count 559bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 560bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param accountId the account being updated 561bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param newCount the number of new messages, or -1 if not being reported (don't update) 562bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @return the report for the updated account, or null if it doesn't exist (e.g. deleted) 5638a574694606f0e5d781334d0d426fc379c51f3edMarc Blank */ 564bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private AccountSyncReport updateAccountReport(long accountId, int newCount) { 565bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // restore the reports if lost 566bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook setupSyncReports(accountId); 567bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook synchronized (mSyncReports) { 568bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountSyncReport report = mSyncReports.get(accountId); 569bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (report == null) { 570bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // discard result - there is no longer an account with this id 571bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "No account to update for id=" + Long.toString(accountId)); 572bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return null; 573bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 574bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 575bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // report found - update it (note - editing the report while in-place in the hashmap) 576bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.prevSyncTime = SystemClock.elapsedRealtime(); 577bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.setNextSyncTime(); 578bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (Email.DEBUG) { 579bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "update account " + report.toString()); 580f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank } 581bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return report; 582f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank } 583f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank } 584f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank 585bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 586bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * when we receive an alarm, update the account sync reports list if necessary 587bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * this will be the case when if we have restarted the process and lost the data 588bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * in the global. 589bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * 590bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * @param restoreIntent the intent with the list 591bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 592bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private void restoreSyncReports(Intent restoreIntent) { 593bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // restore the reports if lost 594bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook setupSyncReports(SYNC_REPORTS_ALL_ACCOUNTS_IF_EMPTY); 595bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook synchronized (mSyncReports) { 596bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long[] accountInfo = restoreIntent.getLongArrayExtra(EXTRA_ACCOUNT_INFO); 597bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (accountInfo == null) { 598bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Log.d(LOG_TAG, "no data in intent to restore"); 599bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return; 600bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 601bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int accountInfoIndex = 0; 602bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int accountInfoLimit = accountInfo.length; 603bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook while (accountInfoIndex < accountInfoLimit) { 604bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long accountId = accountInfo[accountInfoIndex++]; 605bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long prevSync = accountInfo[accountInfoIndex++]; 606bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountSyncReport report = mSyncReports.get(accountId); 607bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (report != null) { 608bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (report.prevSyncTime == 0) { 609bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.prevSyncTime = prevSync; 610bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook report.setNextSyncTime(); 611bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 612bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 613bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 614bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 615bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 616f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank 617bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook class ControllerResults extends Controller.Result { 618bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 619bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void updateMailboxCallback(MessagingException result, long accountId, 620bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long mailboxId, int progress, int numNewMessages, 621bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ArrayList<Long> addedMessages) { 622bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // First, look for authentication failures and notify 623bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook //checkAuthenticationStatus(result, accountId); 624bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (result != null || progress == 100) { 625bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // We only track the inbox here in the service - ignore other mailboxes 626bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long inboxId = Mailbox.findMailboxOfType(MailService.this, 627bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook accountId, Mailbox.TYPE_INBOX); 628bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (mailboxId == inboxId) { 629bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (progress == 100) { 630bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook updateAccountReport(accountId, numNewMessages); 631bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } else { 632bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook updateAccountReport(accountId, -1); 633bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 634bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 635bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 636f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank } 637f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank 638bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 639bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void serviceCheckMailCallback(MessagingException result, long accountId, 640bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long mailboxId, int progress, long tag) { 641bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (result != null || progress == 100) { 642bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (result != null) { 643bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // the checkmail ended in an error. force an update of the refresh 644bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // time, so we don't just spin on this account 645bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook updateAccountReport(accountId, -1); 646bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 647bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE); 648bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook reschedule(alarmManager); 649bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook int serviceId = mStartId; 650bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (tag != 0) { 651bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook serviceId = (int) tag; 652bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 653bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook stopSelf(serviceId); 654bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 655f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank } 656bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 657f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank 658bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public class EmailSyncStatusObserver implements SyncStatusObserver { 659f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank @Override 660bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public void onStatusChanged(int which) { 661bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook // We ignore the argument (we can only get called in one case - when settings change) 662f53490dc86f5a61ab09024dc4938ce8419c61fc5Marc Blank } 663002a1802cad235ffa8f7152e0d0a5a2ebad14f63Ben Komalo } 664002a1802cad235ffa8f7152e0d0a5a2ebad14f63Ben Komalo 665bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static ArrayList<Account> getPopImapAccountList(Context context) { 666bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ArrayList<Account> providerAccounts = new ArrayList<Account>(); 667bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Cursor c = context.getContentResolver().query(Account.CONTENT_URI, Account.ID_PROJECTION, 668bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook null, null, null); 669bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook try { 670bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook while (c.moveToNext()) { 671bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook long accountId = c.getLong(Account.CONTENT_ID_COLUMN); 672bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String protocol = Account.getProtocol(context, accountId); 673bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if ((protocol != null) && ("pop3".equals(protocol) || "imap".equals(protocol))) { 674bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook Account account = Account.restoreAccountWithId(context, accountId); 675bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (account != null) { 676bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook providerAccounts.add(account); 677bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 678bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 679bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 680bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } finally { 681bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook c.close(); 682bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 683bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return providerAccounts; 684bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 685bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 686bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook private static final SingleRunningTask<Context> sReconcilePopImapAccountsSyncExecutor = 687bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook new SingleRunningTask<Context>("ReconcilePopImapAccountsSync") { 688bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook @Override 689bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook protected void runInternal(Context context) { 690bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook android.accounts.Account[] accountManagerAccounts = AccountManager.get(context) 691bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook .getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP); 692bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ArrayList<Account> providerAccounts = getPopImapAccountList(context); 693bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook MailService.reconcileAccountsWithAccountManager(context, providerAccounts, 694bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook accountManagerAccounts, context); 695bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 696bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 697bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook }; 698bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 699bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 700bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Reconcile POP/IMAP accounts. 701bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 702bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static void reconcilePopImapAccountsSync(Context context) { 703bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook sReconcilePopImapAccountsSyncExecutor.run(context); 704bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 705bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 706bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook /** 707bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * Determines whether or not POP/IMAP accounts need reconciling or not. This is a safe operation 708bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook * to perform on the UI thread. 709bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook */ 710bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static boolean hasMismatchInPopImapAccounts(Context context) { 711bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook android.accounts.Account[] accountManagerAccounts = AccountManager.get(context) 712bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook .getAccountsByType(AccountManagerTypes.TYPE_POP_IMAP); 713bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook ArrayList<Account> providerAccounts = getPopImapAccountList(context); 714bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook return AccountReconciler.accountsNeedReconciling( 715bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook context, providerAccounts, accountManagerAccounts); 716bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook } 717bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook 718002a1802cad235ffa8f7152e0d0a5a2ebad14f63Ben Komalo /** 7198a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * See Utility.reconcileAccounts for details 7208a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * @param context The context in which to operate 7218a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * @param emailProviderAccounts the exchange provider accounts to work from 7228a574694606f0e5d781334d0d426fc379c51f3edMarc Blank * @param accountManagerAccounts The account manager accounts to work from 7234e4aba9ebc43c6a83190f3a883fa05bb7d5100b3Marc Blank * @param providerContext the provider's context (in unit tests, this may differ from context) 7248a574694606f0e5d781334d0d426fc379c51f3edMarc Blank */ 725c4cdb11d24c19428dd39f986b00c1a29e75e1505Todd Kennedy @VisibleForTesting 726c4cdb11d24c19428dd39f986b00c1a29e75e1505Todd Kennedy public static void reconcileAccountsWithAccountManager(Context context, 7278a574694606f0e5d781334d0d426fc379c51f3edMarc Blank List<Account> emailProviderAccounts, android.accounts.Account[] accountManagerAccounts, 7284e4aba9ebc43c6a83190f3a883fa05bb7d5100b3Marc Blank Context providerContext) { 7294e4aba9ebc43c6a83190f3a883fa05bb7d5100b3Marc Blank AccountReconciler.reconcileAccounts(context, emailProviderAccounts, accountManagerAccounts, 7304e4aba9ebc43c6a83190f3a883fa05bb7d5100b3Marc Blank providerContext); 7318a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } 7328a574694606f0e5d781334d0d426fc379c51f3edMarc Blank 733bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook public static void setupAccountManagerAccount(Context context, Account account, 734bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook boolean email, boolean calendar, boolean contacts, 7358a574694606f0e5d781334d0d426fc379c51f3edMarc Blank AccountManagerCallback<Bundle> callback) { 7368a574694606f0e5d781334d0d426fc379c51f3edMarc Blank Bundle options = new Bundle(); 7378a574694606f0e5d781334d0d426fc379c51f3edMarc Blank HostAuth hostAuthRecv = HostAuth.restoreHostAuthWithId(context, account.mHostAuthKeyRecv); 738bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook if (hostAuthRecv == null) return; 7398a574694606f0e5d781334d0d426fc379c51f3edMarc Blank // Set up username/password 7408a574694606f0e5d781334d0d426fc379c51f3edMarc Blank options.putString(EasAuthenticatorService.OPTIONS_USERNAME, account.mEmailAddress); 7418a574694606f0e5d781334d0d426fc379c51f3edMarc Blank options.putString(EasAuthenticatorService.OPTIONS_PASSWORD, hostAuthRecv.mPassword); 7428a574694606f0e5d781334d0d426fc379c51f3edMarc Blank options.putBoolean(EasAuthenticatorService.OPTIONS_CONTACTS_SYNC_ENABLED, contacts); 7438a574694606f0e5d781334d0d426fc379c51f3edMarc Blank options.putBoolean(EasAuthenticatorService.OPTIONS_CALENDAR_SYNC_ENABLED, calendar); 7448a574694606f0e5d781334d0d426fc379c51f3edMarc Blank options.putBoolean(EasAuthenticatorService.OPTIONS_EMAIL_SYNC_ENABLED, email); 745bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook String accountType = hostAuthRecv.mProtocol.equals("eas") ? 746bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountManagerTypes.TYPE_EXCHANGE : 747bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountManagerTypes.TYPE_POP_IMAP; 748bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook AccountManager.get(context).addAccount(accountType, null, null, options, null, callback, 749bc47398187c6ffd132435e51d8d61e6ec79a79dbPaul Westbrook null); 7508a574694606f0e5d781334d0d426fc379c51f3edMarc Blank } 7518a574694606f0e5d781334d0d426fc379c51f3edMarc Blank} 752