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