EasSyncService.java revision b9781ea88a58e746b74076ec499e9885e195acc9
1ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/*
2ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Copyright (C) 2008-2009 Marc Blank
3ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed to The Android Open Source Project.
4ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank *
5ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed under the Apache License, Version 2.0 (the "License");
6ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * you may not use this file except in compliance with the License.
7ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * You may obtain a copy of the License at
8ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank *
9ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank *      http://www.apache.org/licenses/LICENSE-2.0
10ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank *
11ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Unless required by applicable law or agreed to in writing, software
12ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * distributed under the License is distributed on an "AS IS" BASIS,
13ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * See the License for the specific language governing permissions and
15ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * limitations under the License.
16ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */
17ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
18ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpackage com.android.exchange;
19ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
201b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blankimport com.android.email.codec.binary.Base64;
21ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.email.mail.AuthenticationFailedException;
22ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.email.mail.MessagingException;
2367698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Account;
2467698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.AccountColumns;
2567698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Attachment;
2667698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.AttachmentColumns;
2767698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.HostAuth;
2867698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Mailbox;
2967698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.MailboxColumns;
3067698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Message;
317c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.AbstractSyncAdapter;
3248af7392c82262d17700e3fbdccf3a582809d449Marc Blankimport com.android.exchange.adapter.AccountSyncAdapter;
337c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.ContactsSyncAdapter;
347c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.EmailSyncAdapter;
357c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.FolderSyncParser;
367c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.PingParser;
377c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.Serializer;
387c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.Tags;
397c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.Parser.EasParserException;
40ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
418047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.Header;
4200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.HttpEntity;
4300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.HttpResponse;
441b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blankimport org.apache.http.HttpStatus;
458047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.HttpClient;
468047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.methods.HttpOptions;
4700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.client.methods.HttpPost;
488047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.methods.HttpRequestBase;
49e44d5875af006f4217718a1c0fc0e235af3863afMarc Blankimport org.apache.http.conn.ClientConnectionManager;
508047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.entity.ByteArrayEntity;
5100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.impl.client.DefaultHttpClient;
528047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.BasicHttpParams;
538047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.HttpConnectionParams;
548047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.HttpParams;
5500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank
56ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentResolver;
57ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport android.content.ContentUris;
58ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentValues;
59ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.Context;
60ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.database.Cursor;
61d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blankimport android.os.RemoteException;
6274c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blankimport android.os.SystemClock;
63ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
6400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.File;
6500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.FileOutputStream;
6600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException;
6700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream;
6800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URI;
6900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URLEncoder;
70e44d5875af006f4217718a1c0fc0e235af3863afMarc Blankimport java.security.cert.CertificateException;
7100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList;
72ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport java.util.HashMap;
7300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank
74ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankpublic class EasSyncService extends AbstractSyncService {
751b275b9408d5b856e2482fa3951827489e9585ccMarc Blank    private static final String EMAIL_WINDOW_SIZE = "5";
76726d60d9b758f0383f8f8481190fc1a638427209Marc Blank    public static final String PIM_WINDOW_SIZE = "5";
77ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID =
78ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SERVER_ID + "=?";
799d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private static final String WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING =
809d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL +
81f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank        '=' + Mailbox.CHECK_INTERVAL_PING;
8222bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank    private static final String AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX = " AND " +
83f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank        MailboxColumns.SYNC_INTERVAL + " IN (" + Mailbox.CHECK_INTERVAL_PING +
84f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank        ',' + Mailbox.CHECK_INTERVAL_PUSH + ") AND " + MailboxColumns.TYPE + "!=\"" +
857c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank        Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + '\"';
869d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private static final String WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX =
879d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL +
88f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank        '=' + Mailbox.CHECK_INTERVAL_PUSH_HOLD;
898047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    static private final int CHUNK_SIZE = 16*1024;
908047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
918047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    static private final String PING_COMMAND = "Ping";
92c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int COMMAND_TIMEOUT = 20*SECONDS;
93c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
941b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    /**
951b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * We start with an 8 minute timeout, and increase/decrease by 3 minutes at a time.  There's
961b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * no point having a timeout shorter than 5 minutes, I think; at that point, we can just let
971b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * the ping exception out.  The maximum I use is 17 minutes, which is really an empirical
981b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * choice; too long and we risk silent connection loss and loss of push for that period.  Too
991b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * short and we lose efficiency/battery life.
1001b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     *
1011b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * If we ever have to drop the ping timeout, we'll never increase it again.  There's no point
1021b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * going into hysteresis; the NAT timeout isn't going to change without a change in connection,
1031b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * which will cause the sync service to be restarted at the starting heartbeat and going through
1041b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * the process again.
1051b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     */
1061b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    static private final int PING_MINUTES = 60; // in seconds
1071b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    static private final int PING_FUDGE_LOW = 10;
1081b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    static private final int PING_STARTING_HEARTBEAT = (8*PING_MINUTES)-PING_FUDGE_LOW;
1091b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    static private final int PING_MIN_HEARTBEAT = (5*PING_MINUTES)-PING_FUDGE_LOW;
1101b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    static private final int PING_MAX_HEARTBEAT = (17*PING_MINUTES)-PING_FUDGE_LOW;
1111b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    static private final int PING_HEARTBEAT_INCREMENT = 3*PING_MINUTES;
1127672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank    static private final int PING_FORCE_HEARTBEAT = 2*PING_MINUTES;
1131b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
1148d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank    static private final int PROTOCOL_PING_STATUS_UNAVAILABLE = -1;
1151b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    static private final int PROTOCOL_PING_STATUS_COMPLETED = 1;
11696293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank
117c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    // Fallbacks (in minutes) for ping loop failures
118252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank    static private final int MAX_PING_FAILURES = 1;
119c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int PING_FALLBACK_INBOX = 5;
12027cf341571fac3d8dbe866f503c34fc31e02bf85Marc Blank    static private final int PING_FALLBACK_PIM = 25;
121d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
122ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    // Reasonable default
123ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mProtocolVersion = "2.5";
12400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank    public Double mProtocolVersionDouble;
12585f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank    protected String mDeviceId = null;
1269d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String mDeviceType = "Android";
1271b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private String mAuthString = null;
1281b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private String mCmdString = null;
129ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mHostAddress;
130ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mUserName;
131ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mPassword;
1321b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private boolean mSsl = true;
1335843b85178a359446f81770ed7734604a1b2fa7dMarc Blank    private boolean mTrustSsl = false;
134ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public ContentResolver mContentResolver;
1351b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private String[] mBindArguments = new String[2];
1361b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private ArrayList<String> mPingChangeList;
1371b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private HttpPost mPendingPost = null;
1381b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    // The ping time (in seconds)
1391b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private int mPingHeartbeat = PING_STARTING_HEARTBEAT;
1401b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    // The longest successful ping heartbeat
1411b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private int mPingHighWaterMark = 0;
1421b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    // Whether we've ever lowered the heartbeat
1431b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private boolean mPingHeartbeatDropped = false;
144b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank    // Whether a POST was aborted due to watchdog timeout
145b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank    private boolean mAborted = false;
146ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
147ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasSyncService(Context _context, Mailbox _mailbox) {
148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(_context, _mailbox);
149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mContentResolver = _context.getContentResolver();
150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HostAuth ha = HostAuth.restoreHostAuthWithId(_context, mAccount.mHostAuthKeyRecv);
151ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0;
152e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        mTrustSsl = (ha.mFlags & HostAuth.FLAG_TRUST_ALL_CERTIFICATES) != 0;
153ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
154ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
155ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    private EasSyncService(String prefix) {
156ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(prefix);
157ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
158ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasSyncService() {
160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        this("EAS Validation");
161ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
164ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void ping() {
1651b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        userLog("Alarm ping received!");
1661b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        synchronized(getSynchronizer()) {
1671b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            if (mPendingPost != null) {
1681b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                userLog("Aborting pending POST!");
169b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                mAborted = true;
1701b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                mPendingPost.abort();
1711b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            }
172ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
173ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
174ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
176ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void stop() {
177ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mStop = true;
178c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        synchronized(getSynchronizer()) {
179c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            if (mPendingPost != null) {
180c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                mPendingPost.abort();
181c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            }
182c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
1835c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank    }
184ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
1851b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    /**
1861b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * Determine whether an HTTP code represents an authentication error
1871b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * @param code the HTTP code returned by the server
1881b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * @return whether or not the code represents an authentication error
1891b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     */
190f3ae2f9ee2ced1afc5cac4ebad125161726b6c0bMarc Blank    protected boolean isAuthError(int code) {
1917ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        return ((code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN));
192368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
193368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
194fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank    @Override
195ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void validateAccount(String hostAddress, String userName, String password, int port,
196e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            boolean ssl, boolean trustCertificates, Context context) throws MessagingException {
197ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
1980a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank            userLog("Testing EAS: ", hostAddress, ", ", userName, ", ssl = ", ssl ? "1" : "0");
199ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            EasSyncService svc = new EasSyncService("%TestAccount%");
2009d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            svc.mContext = context;
201ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mHostAddress = hostAddress;
202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mUserName = userName;
203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mPassword = password;
204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mSsl = ssl;
205e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            svc.mTrustSsl = trustCertificates;
2069d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            svc.mDeviceId = SyncManager.getDeviceId();
2078047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            HttpResponse resp = svc.sendHttpClientOptions();
2088047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            int code = resp.getStatusLine().getStatusCode();
2099d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            userLog("Validation (OPTIONS) response: " + code);
2101b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            if (code == HttpStatus.SC_OK) {
211ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // No exception means successful validation
212ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Validation successful");
213ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return;
214ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
215368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            if (isAuthError(code)) {
216ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Authentication failed");
217ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                throw new AuthenticationFailedException("Validation failed");
218ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
219ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // TODO Need to catch other kinds of errors (e.g. policy) For now, report the code.
2200a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                userLog("Validation failed, reporting I/O error: ", code);
221ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                throw new MessagingException(MessagingException.IOERROR);
222ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
223ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (IOException e) {
224e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            Throwable cause = e.getCause();
225e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            if (cause != null && cause instanceof CertificateException) {
226e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank                userLog("CertificateException caught: ", e.getMessage());
227e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank                throw new MessagingException(MessagingException.GENERAL_SECURITY);
228e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            }
229e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            userLog("IOException caught: ", e.getMessage());
230ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            throw new MessagingException(MessagingException.IOERROR);
231ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
232ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
233ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
234ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
23581d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank    private void doStatusCallback(long messageId, long attachmentId, int status) {
236d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        try {
237fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().loadAttachmentStatus(messageId, attachmentId, status, 0);
238fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e) {
239d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            // No danger if the client is no longer around
240d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        }
241d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    }
242ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
24381d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank    private void doProgressCallback(long messageId, long attachmentId, int progress) {
244d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        try {
245fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().loadAttachmentStatus(messageId, attachmentId,
246fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                    EmailServiceStatus.IN_PROGRESS, progress);
247fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e) {
248d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            // No danger if the client is no longer around
249d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        }
250d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    }
251d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
252842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    public File createUniqueFileInternal(String dir, String filename) {
253842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        File directory;
254842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (dir == null) {
255842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory = mContext.getFilesDir();
256842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        } else {
257842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory = new File(dir);
258842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
259842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (!directory.exists()) {
260842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory.mkdirs();
261842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
262842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        File file = new File(directory, filename);
263842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (!file.exists()) {
264842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            return file;
265842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
266842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        // Get the extension of the file, if any.
267842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        int index = filename.lastIndexOf('.');
268842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        String name = filename;
269842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        String extension = "";
270842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (index != -1) {
271842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            name = filename.substring(0, index);
272842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            extension = filename.substring(index);
273842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
274842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        for (int i = 2; i < Integer.MAX_VALUE; i++) {
275842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            file = new File(directory, name + '-' + i + extension);
276842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            if (!file.exists()) {
277842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                return file;
278842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            }
279842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
280842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        return null;
281842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    }
282842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank
283d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    /**
284d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * Loads an attachment, based on the PartRequest passed in.  The PartRequest is basically our
285d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * wrapper for Attachment
286d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * @param req the part (attachment) to be retrieved
287d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * @throws IOException
288d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     */
289842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    protected void getAttachment(PartRequest req) throws IOException {
290d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        Attachment att = req.att;
291b0ce70f8d18dfe14fdd72be528d89eda1ba229feMarc Blank        Message msg = Message.restoreMessageWithId(mContext, att.mMessageKey);
29281d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank        doProgressCallback(msg.mId, att.mId, 0);
293ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        DefaultHttpClient client = new DefaultHttpClient();
294d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        String us = makeUriString("GetAttachment", "&AttachmentName=" + att.mLocation);
295ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HttpPost method = new HttpPost(URI.create(us));
296ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        method.setHeader("Authorization", mAuthString);
297ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
298ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HttpResponse res = client.execute(method);
299ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        int status = res.getStatusLine().getStatusCode();
3001b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        if (status == HttpStatus.SC_OK) {
301ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            HttpEntity e = res.getEntity();
302ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int len = (int)e.getContentLength();
303ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String type = e.getContentType().getValue();
304ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            InputStream is = res.getEntity().getContent();
305bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler            File f = (req.destination != null)
306bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    ? new File(req.destination)
307bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    : createUniqueFileInternal(req.destination, att.mFileName);
308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (f != null) {
309bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                // Ensure that the target directory exists
310bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                File destDir = f.getParentFile();
311bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                if (!destDir.exists()) {
312bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    destDir.mkdirs();
313bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                }
314ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                FileOutputStream os = new FileOutputStream(f);
315ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (len > 0) {
316ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    try {
317ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartRequest = req;
318ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        byte[] bytes = new byte[CHUNK_SIZE];
319ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        int length = len;
320ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        while (len > 0) {
321ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int n = (len > CHUNK_SIZE ? CHUNK_SIZE : len);
322ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int read = is.read(bytes, 0, n);
323ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            os.write(bytes, 0, read);
324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            len -= read;
325d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                            int pct = ((length - len) * 100 / length);
32681d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank                            doProgressCallback(msg.mId, att.mId, pct);
327ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
328ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } finally {
329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartRequest = null;
330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                os.flush();
333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                os.close();
334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
335d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                // EmailProvider will throw an exception if we try to update an unsaved attachment
336d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                if (att.isSaved()) {
337bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    String contentUriString = (req.contentUriString != null)
338bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                            ? req.contentUriString
339bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                            : "file://" + f.getAbsolutePath();
340d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    ContentValues cv = new ContentValues();
341bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    cv.put(AttachmentColumns.CONTENT_URI, contentUriString);
342d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    cv.put(AttachmentColumns.MIME_TYPE, type);
343d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    att.update(mContext, cv);
34481d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank                    doStatusCallback(msg.mId, att.mId, EmailServiceStatus.SUCCESS);
345d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
346ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
347d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        } else {
34881d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank            doStatusCallback(msg.mId, att.mId, EmailServiceStatus.MESSAGE_NOT_FOUND);
349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
350ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
351ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
352147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank    @SuppressWarnings("deprecation")
3539d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String makeUriString(String cmd, String extra) throws IOException {
354ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank         // Cache the authentication string and the command string
355ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String safeUserName = URLEncoder.encode(mUserName);
356ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (mAuthString == null) {
357ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String cs = mUserName + ':' + mPassword;
3581b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            mAuthString = "Basic " + new String(Base64.encodeBase64(cs.getBytes()));
359ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId + "&DeviceType="
360ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    + mDeviceType;
361ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
362e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        String us = (mSsl ? (mTrustSsl ? "httpts" : "https") : "http") + "://" + mHostAddress +
363ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            "/Microsoft-Server-ActiveSync";
364ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (cmd != null) {
365ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            us += "?Cmd=" + cmd + mCmdString;
366ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
367ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (extra != null) {
368ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            us += extra;
369ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
370ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return us;
371ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
372ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3738047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private void setHeaders(HttpRequestBase method) {
3748047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("Authorization", mAuthString);
3758047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("MS-ASProtocolVersion", mProtocolVersion);
3768047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("Connection", "keep-alive");
3778047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("User-Agent", mDeviceType + '/' + Eas.VERSION);
3788047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
379ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
380e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank    private ClientConnectionManager getClientConnectionManager() {
381e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        return SyncManager.getClientConnectionManager();
382e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank    }
383e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank
3848047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private HttpClient getHttpClient(int timeout) {
3858047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpParams params = new BasicHttpParams();
3861b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        HttpConnectionParams.setConnectionTimeout(params, 15*SECONDS);
3878047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpConnectionParams.setSoTimeout(params, timeout);
388e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        HttpConnectionParams.setSocketBufferSize(params, 8192);
389e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        HttpClient client = new DefaultHttpClient(getClientConnectionManager(), params);
390e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        return client;
3918047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
392ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3938047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    protected HttpResponse sendHttpClientPost(String cmd, byte[] bytes) throws IOException {
3941b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        return sendHttpClientPost(cmd, new ByteArrayEntity(bytes), COMMAND_TIMEOUT);
3959e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank    }
3969e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank
3979e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank    protected HttpResponse sendHttpClientPost(String cmd, HttpEntity entity) throws IOException {
3981b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        return sendHttpClientPost(cmd, entity, COMMAND_TIMEOUT);
3991b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    }
4001b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
4012033dfc4e2e6e352b34565112266084d72c443f1Marc Blank    protected HttpResponse sendPing(byte[] bytes, int heartbeat) throws IOException {
4021b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank       Thread.currentThread().setName(mAccount.mDisplayName + ": Ping");
4031b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank       if (Eas.USER_LOG) {
4042033dfc4e2e6e352b34565112266084d72c443f1Marc Blank           userLog("Send ping, timeout: " + heartbeat + "s, high: " + mPingHighWaterMark + 's');
4051b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank       }
4062033dfc4e2e6e352b34565112266084d72c443f1Marc Blank       return sendHttpClientPost(PING_COMMAND, new ByteArrayEntity(bytes), (heartbeat+5)*SECONDS);
4071b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    }
4081b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
4091b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    protected HttpResponse sendHttpClientPost(String cmd, HttpEntity entity, int timeout)
4101b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            throws IOException {
4111b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        HttpClient client = getHttpClient(timeout);
41285f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank
41385f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        // Split the mail sending commands
41485f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        String extra = null;
41585f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        boolean msg = false;
41685f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) {
4175843b85178a359446f81770ed7734604a1b2fa7dMarc Blank            int cmdLength = cmd.indexOf('&');
41885f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank            extra = cmd.substring(cmdLength);
41985f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank            cmd = cmd.substring(0, cmdLength);
42085f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank            msg = true;
42185f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        } else if (cmd.startsWith("SendMail&")) {
42285f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank            msg = true;
42385f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        }
42485f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank
42585f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        String us = makeUriString(cmd, extra);
4268047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpPost method = new HttpPost(URI.create(us));
42785f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        if (msg) {
4288047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            method.setHeader("Content-Type", "message/rfc822");
4298047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        } else {
4308047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml");
431ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
4328047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        setHeaders(method);
4339e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank        method.setEntity(entity);
434c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        synchronized(getSynchronizer()) {
435c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            mPendingPost = method;
4368d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank            SyncManager.runAsleep(mMailboxId, timeout+(10*SECONDS));
437c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
438c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        try {
439c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            return client.execute(method);
440c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        } finally {
441c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            synchronized(getSynchronizer()) {
4422033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                SyncManager.runAwake(mMailboxId);
443c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                mPendingPost = null;
444c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            }
445c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
4468047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
4478047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
4488047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    protected HttpResponse sendHttpClientOptions() throws IOException {
4498047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpClient client = getHttpClient(COMMAND_TIMEOUT);
4508047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        String us = makeUriString("OPTIONS", null);
4518047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpOptions method = new HttpOptions(URI.create(us));
4528047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        setHeaders(method);
4538047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        return client.execute(method);
454ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
455ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
456ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String getTargetCollectionClassFromCursor(Cursor c) {
457ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
458ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (type == Mailbox.TYPE_CONTACTS) {
459ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Contacts";
460ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else if (type == Mailbox.TYPE_CALENDAR) {
461ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Calendar";
462ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else {
463ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Email";
464ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
465ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
466ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
467ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /**
468ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * Performs FolderSync
469ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     *
470ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @throws IOException
471ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @throws EasParserException
472ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
473a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank    public void runAccountMailbox() throws IOException, EasParserException {
474fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        // Initialize exit status to success
475fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        mExitStatus = EmailServiceStatus.SUCCESS;
476ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
477fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            try {
478fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                SyncManager.callback()
479fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                    .syncMailboxListStatus(mAccount.mId, EmailServiceStatus.IN_PROGRESS, 0);
480fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            } catch (RemoteException e1) {
481fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                // Don't care if this fails
482fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
483fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
484ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mAccount.mSyncKey == null) {
485ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mAccount.mSyncKey = "0";
4861b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Account syncKey INIT to 0");
4879387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                ContentValues cv = new ContentValues();
4889387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
4899387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                mAccount.update(mContext, cv);
490ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
491ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
4929d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            boolean firstSync = mAccount.mSyncKey.equals("0");
4939d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            if (firstSync) {
4941b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Initial FolderSync");
4951b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            }
4961b275b9408d5b856e2482fa3951827489e9585ccMarc Blank
4974626078bf9d930b2007162db142b5961b38e2166Marc Blank            // When we first start up, change all mailboxes to push.
498ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ContentValues cv = new ContentValues();
499f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank            cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
500ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
5019d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING,
5029d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    new String[] {Long.toString(mAccount.mId)}) > 0) {
5039d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                SyncManager.kick("change ping boxes to push");
504ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
505ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
5060f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            // Determine our protocol version, if we haven't already
5070f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            if (mAccount.mProtocolVersion == null) {
5081b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Determine EAS protocol version");
5098047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                HttpResponse resp = sendHttpClientOptions();
5108047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                int code = resp.getStatusLine().getStatusCode();
5110a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                userLog("OPTIONS response: ", code);
5121b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                if (code == HttpStatus.SC_OK) {
513647aa5f1f229947d982548f698c4533fe538f884Marc Blank                    Header header = resp.getFirstHeader("MS-ASProtocolCommands");
514647aa5f1f229947d982548f698c4533fe538f884Marc Blank                    userLog(header.getValue());
515647aa5f1f229947d982548f698c4533fe538f884Marc Blank                    header = resp.getFirstHeader("ms-asprotocolversions");
5168047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    String versions = header.getValue();
5178047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    if (versions != null) {
5188047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        if (versions.contains("12.0")) {
5198047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                            mProtocolVersion = "12.0";
520ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
5218047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
5228047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        mAccount.mProtocolVersion = mProtocolVersion;
5238047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        userLog(versions);
5240a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                        userLog("Using version ", mProtocolVersion);
525ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } else {
5268047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        errorLog("No protocol versions in OPTIONS response");
527ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        throw new IOException();
528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
5298047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                } else {
5308047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    errorLog("OPTIONS command failed; throwing IOException");
5318047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    throw new IOException();
5320f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
5330f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            }
5341b275b9408d5b856e2482fa3951827489e9585ccMarc Blank
53577424af660458104b732bdcb718874b17d0cab3aMarc Blank            // Change all pushable boxes to push when we start the account mailbox
53677424af660458104b732bdcb718874b17d0cab3aMarc Blank            if (mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH) {
53777424af660458104b732bdcb718874b17d0cab3aMarc Blank                cv = new ContentValues();
538f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank                cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
53977424af660458104b732bdcb718874b17d0cab3aMarc Blank                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
54077424af660458104b732bdcb718874b17d0cab3aMarc Blank                        SyncManager.WHERE_IN_ACCOUNT_AND_PUSHABLE,
54177424af660458104b732bdcb718874b17d0cab3aMarc Blank                        new String[] {Long.toString(mAccount.mId)}) > 0) {
54277424af660458104b732bdcb718874b17d0cab3aMarc Blank                    userLog("Push account; set pushable boxes to push...");
54377424af660458104b732bdcb718874b17d0cab3aMarc Blank                }
54477424af660458104b732bdcb718874b17d0cab3aMarc Blank            }
54577424af660458104b732bdcb718874b17d0cab3aMarc Blank
54677424af660458104b732bdcb718874b17d0cab3aMarc Blank            while (!mStop) {
5470a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                 userLog("Sending Account syncKey: ", mAccount.mSyncKey);
5488047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 Serializer s = new Serializer();
5498047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
55077424af660458104b732bdcb718874b17d0cab3aMarc Blank                     .text(mAccount.mSyncKey).end().end().done();
5518047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 HttpResponse resp = sendHttpClientPost("FolderSync", s.toByteArray());
5528047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 if (mStop) break;
5538047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 int code = resp.getStatusLine().getStatusCode();
5541b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                 if (code == HttpStatus.SC_OK) {
5558047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     HttpEntity entity = resp.getEntity();
5568047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     int len = (int)entity.getContentLength();
5578047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     if (len > 0) {
5588047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         InputStream is = entity.getContent();
5598047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         // Returns true if we need to sync again
56048af7392c82262d17700e3fbdccf3a582809d449Marc Blank                         if (new FolderSyncParser(is, new AccountSyncAdapter(mMailbox, this))
56148af7392c82262d17700e3fbdccf3a582809d449Marc Blank                                 .parse()) {
5628047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                             continue;
5638047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         }
5648047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     }
5651b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                 } else if (isAuthError(code)) {
5661b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    mExitStatus = EXIT_LOGIN_FAILURE;
5670f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } else {
5680a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                    userLog("FolderSync response error: ", code);
5690f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
570ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
5719d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                // Change all push/hold boxes to push
5729d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                cv = new ContentValues();
5739d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
5749d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
5759d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX,
5769d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        new String[] {Long.toString(mAccount.mId)}) > 0) {
5779d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    userLog("Set push/hold boxes to push...");
5789d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                }
5799d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
5800f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                try {
5810f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    SyncManager.callback()
5829d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        .syncMailboxListStatus(mAccount.mId, mExitStatus, 0);
5830f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } catch (RemoteException e1) {
5840f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    // Don't care if this fails
5850f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
586fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
5870f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                // Wait for push notifications.
5880f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                String threadName = Thread.currentThread().getName();
5890f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                try {
5900f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    runPingLoop();
5910f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } catch (StaleFolderListException e) {
5920f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    // We break out if we get told about a stale folder list
5930f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    userLog("Ping interrupted; folder list requires sync...");
5940f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } finally {
5950f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    Thread.currentThread().setName(threadName);
5960f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
597ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
5980f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank         } catch (IOException e) {
599fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // We catch this here to send the folder sync status callback
600fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // A folder sync failed callback will get sent from run()
601fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            try {
6024d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                if (!mStop) {
6034d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    SyncManager.callback()
6044d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                        .syncMailboxListStatus(mAccount.mId,
6054d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                                EmailServiceStatus.CONNECTION_ERROR, 0);
6064d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                }
607fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            } catch (RemoteException e1) {
608fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                // Don't care if this fails
609fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
61096293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank            throw e;
611ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
612ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
613ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
614c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    void pushFallback(long mailboxId) {
615c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
616c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        ContentValues cv = new ContentValues();
6174626078bf9d930b2007162db142b5961b38e2166Marc Blank        int mins = PING_FALLBACK_PIM;
618c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        if (mailbox.mType == Mailbox.TYPE_INBOX) {
6194626078bf9d930b2007162db142b5961b38e2166Marc Blank            mins = PING_FALLBACK_INBOX;
620c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
6214626078bf9d930b2007162db142b5961b38e2166Marc Blank        cv.put(Mailbox.SYNC_INTERVAL, mins);
6224626078bf9d930b2007162db142b5961b38e2166Marc Blank        mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
6234626078bf9d930b2007162db142b5961b38e2166Marc Blank                cv, null, null);
62427cf341571fac3d8dbe866f503c34fc31e02bf85Marc Blank        errorLog("*** PING ERROR LOOP: Set " + mailbox.mDisplayName + " to " + mins + " min sync");
6254626078bf9d930b2007162db142b5961b38e2166Marc Blank        SyncManager.kick("push fallback");
626368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
627368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
628ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    void runPingLoop() throws IOException, StaleFolderListException {
6291b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        int pingHeartbeat = mPingHeartbeat;
63074c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank        userLog("runPingLoop");
631ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // Do push for all sync services here
632c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        long endTime = System.currentTimeMillis() + (30*MINUTES);
6331b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        HashMap<String, Integer> pingErrorMap = new HashMap<String, Integer>();
6342033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        ArrayList<String> readyMailboxes = new ArrayList<String>();
6352033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        ArrayList<String> notReadyMailboxes = new ArrayList<String>();
6367672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank        int pingWaitCount = 0;
6378d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
6387ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        while ((System.currentTimeMillis() < endTime) && !mStop) {
639ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Count of pushable mailboxes
640ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int pushCount = 0;
641ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Count of mailboxes that can be pushed right now
642ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int canPushCount = 0;
6438a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank            // Count of uninitialized boxes
6448a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank            int uninitCount = 0;
6458a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank
6467c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            Serializer s = new Serializer();
647ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
64822bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                    MailboxColumns.ACCOUNT_KEY + '=' + mAccount.mId +
64922bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                    AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX, null, null);
6502033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            notReadyMailboxes.clear();
6512033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            readyMailboxes.clear();
652ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            try {
653ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Loop through our pushed boxes seeing what is available to push
654ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                while (c.moveToNext()) {
655ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    pushCount++;
656ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // Two requirements for push:
657ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // 1) SyncManager tells us the mailbox is syncable (not running, not stopped)
658ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // 2) The syncKey isn't "0" (i.e. it's synced at least once)
659ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
66077424af660458104b732bdcb718874b17d0cab3aMarc Blank                    int pingStatus = SyncManager.pingStatus(mailboxId);
66177424af660458104b732bdcb718874b17d0cab3aMarc Blank                    String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
66277424af660458104b732bdcb718874b17d0cab3aMarc Blank                    if (pingStatus == SyncManager.PING_STATUS_OK) {
663a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank
664ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN);
6657ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                        if ((syncKey == null) || syncKey.equals("0")) {
666a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank                            // We can't push until the initial sync is done
66777424af660458104b732bdcb718874b17d0cab3aMarc Blank                            pushCount--;
6688a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank                            uninitCount++;
669ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            continue;
670ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
671ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank
672ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        if (canPushCount++ == 0) {
673ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            // Initialize the Ping command
67496293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank                            s.start(Tags.PING_PING)
67505381a6662f28609e8005023515abb82af00e1d4Marc Blank                                .data(Tags.PING_HEARTBEAT_INTERVAL,
6761b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                                        Integer.toString(pingHeartbeat))
6777c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                                .start(Tags.PING_FOLDERS);
678ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
6791b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
680ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String folderClass = getTargetCollectionClassFromCursor(c);
6817c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                        s.start(Tags.PING_FOLDER)
6827c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .data(Tags.PING_ID, c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN))
6837c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .data(Tags.PING_CLASS, folderClass)
6847c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .end();
6852033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                        readyMailboxes.add(mailboxName);
6867ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                    } else if ((pingStatus == SyncManager.PING_STATUS_RUNNING) ||
6877ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                            (pingStatus == SyncManager.PING_STATUS_WAITING)) {
6882033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                        notReadyMailboxes.add(mailboxName);
68977424af660458104b732bdcb718874b17d0cab3aMarc Blank                    } else if (pingStatus == SyncManager.PING_STATUS_UNABLE) {
69077424af660458104b732bdcb718874b17d0cab3aMarc Blank                        pushCount--;
6910a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                        userLog(mailboxName, " in error state; ignore");
69277424af660458104b732bdcb718874b17d0cab3aMarc Blank                        continue;
693ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
694ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
695ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } finally {
696ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                c.close();
697ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
698ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
6992033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            if (Eas.USER_LOG) {
7002033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                if (!notReadyMailboxes.isEmpty()) {
7012033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                    userLog("Ping not ready for: " + notReadyMailboxes);
7022033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                }
7032033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                if (!readyMailboxes.isEmpty()) {
7042033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                    userLog("Ping ready for: " + readyMailboxes);
7052033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                }
7062033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            }
7072033dfc4e2e6e352b34565112266084d72c443f1Marc Blank
7087672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank            // If we've waited 10 seconds or more, just ping with whatever boxes are ready
7097672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank            // But use a shorter than normal heartbeat
7102033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            boolean forcePing = !notReadyMailboxes.isEmpty() && (pingWaitCount > 5);
7117672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank
7127672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank            if ((canPushCount > 0) && ((canPushCount == pushCount) || forcePing)) {
7137672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                // If all pingable boxes are ready for push, send Ping to the server
7147c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.end().end().done();
7157672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                pingWaitCount = 0;
7167c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank
7171b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                // If we've been stopped, this is a good time to return
7184d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                if (mStop) return;
719368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
72074c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank                long pingTime = SystemClock.elapsedRealtime();
7211b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                try {
7221b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    // Send the ping, wrapped by appropriate timeout/alarm
7237672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                    if (forcePing) {
7247672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                        userLog("Forcing ping after waiting for all boxes to be ready");
7257672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                    }
7267672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                    HttpResponse res =
7277672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                        sendPing(s.toByteArray(), forcePing ? PING_FORCE_HEARTBEAT : pingHeartbeat);
728c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
7291b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    int code = res.getStatusLine().getStatusCode();
7301b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    userLog("Ping response: ", code);
731368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
7321b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    // Return immediately if we've been asked to stop during the ping
7331b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    if (mStop) {
7341b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        userLog("Stopping pingLoop");
7351b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        return;
7361b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    }
7371b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
7381b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    if (code == HttpStatus.SC_OK) {
7391b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        HttpEntity e = res.getEntity();
7401b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        int len = (int)e.getContentLength();
7411b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        InputStream is = res.getEntity().getContent();
7421b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        if (len > 0) {
7438d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            int pingResult = parsePingResult(is, mContentResolver, pingErrorMap);
7447672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                            // If our ping completed (status = 1), and we weren't forced and we're
7457672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                            // not at the maximum, try increasing timeout by two minutes
7468d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            if (pingResult == PROTOCOL_PING_STATUS_COMPLETED && !forcePing) {
7477672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                if (pingHeartbeat > mPingHighWaterMark) {
7487672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    mPingHighWaterMark = pingHeartbeat;
7497672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    userLog("Setting high water mark at: ", mPingHighWaterMark);
7507672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                }
7517672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                if ((pingHeartbeat < PING_MAX_HEARTBEAT) &&
7527672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                        !mPingHeartbeatDropped) {
7537672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    pingHeartbeat += PING_HEARTBEAT_INCREMENT;
7547672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    if (pingHeartbeat > PING_MAX_HEARTBEAT) {
7557672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                        pingHeartbeat = PING_MAX_HEARTBEAT;
7567672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    }
7577672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    userLog("Increasing ping heartbeat to ", pingHeartbeat, "s");
7581b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                                }
7591b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            }
7601b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        } else {
7611b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            userLog("Ping returned empty result; throwing IOException");
7621b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            throw new IOException();
7631b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        }
7641b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    } else if (isAuthError(code)) {
7651b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        mExitStatus = EXIT_LOGIN_FAILURE;
7661b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        userLog("Authorization error during Ping: ", code);
767ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        throw new IOException();
768ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
7691b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                } catch (IOException e) {
770252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank                    String message = e.getMessage();
7711b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    // If we get the exception that is indicative of a NAT timeout and if we
7721b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    // haven't yet "fixed" the timeout, back off by two minutes and "fix" it
773252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank                    boolean hasMessage = message != null;
774252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank                    userLog("IOException runPingLoop: " + (hasMessage ? message : "[no message]"));
775b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                    if (mAborted || (hasMessage && message.contains("reset by peer"))) {
776b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                        long pingLength = SystemClock.elapsedRealtime() - pingTime;
7777ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                        if ((pingHeartbeat > PING_MIN_HEARTBEAT) &&
7787ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                                (pingHeartbeat > mPingHighWaterMark)) {
7791b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            pingHeartbeat -= PING_HEARTBEAT_INCREMENT;
7801b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            mPingHeartbeatDropped = true;
7811b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            if (pingHeartbeat < PING_MIN_HEARTBEAT) {
7821b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                                pingHeartbeat = PING_MIN_HEARTBEAT;
7831b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            }
7841b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            userLog("Decreased ping heartbeat to ", pingHeartbeat, "s");
785b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                        } else if (mAborted || (pingLength < 2000)) {
786b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                            userLog("Abort or NAT type return < 2 seconds; throwing IOException");
78774c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank                            throw e;
78874c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank                        } else {
78974c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank                            userLog("NAT type IOException > 2 seconds?");
7901b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        }
7911b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    } else {
7921b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        throw e;
7931b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    }
794ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
7952033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            } else if (forcePing) {
7962033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                // In this case, there aren't any boxes that are pingable, but there are boxes
7972033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                // waiting (for IOExceptions)
7982033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                userLog("pingLoop waiting 60s for any pingable boxes");
7992033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                sleep(60*SECONDS, true);
800ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else if (pushCount > 0) {
8011ec0390ce7c27890a46f877d858f7ac6b6a8920cMarc Blank                // If we want to Ping, but can't just yet, wait a little bit
8021ec0390ce7c27890a46f877d858f7ac6b6a8920cMarc Blank                // TODO Change sleep to wait and use notify from SyncManager when a sync ends
8032033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                sleep(2*SECONDS, false);
8047672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                pingWaitCount++;
8052033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                //userLog("pingLoop waited 2s for: ", (pushCount - canPushCount), " box(es)");
8068a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank            } else if (uninitCount > 0) {
8078a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank                // In this case, we're doing an initial sync of at least one mailbox.  Since this
8088a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank                // is typically a one-time case, I'm ok with trying again every 10 seconds until
8098a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank                // we're in one of the other possible states.
8102033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                userLog("pingLoop waiting for initial sync of ", uninitCount, " box(es)");
8112033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                sleep(10*SECONDS, true);
812ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
813c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                // We've got nothing to do, so we'll check again in 30 minutes at which time
8141b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                // we'll update the folder list.  Let the device sleep in the meantime...
8152033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                userLog("pingLoop sleeping for 30m");
8162033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                sleep(30*MINUTES, true);
817ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
818ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
819ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
820ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
8212033dfc4e2e6e352b34565112266084d72c443f1Marc Blank    void sleep(long ms, boolean runAsleep) {
8222033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        if (runAsleep) {
8232033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            SyncManager.runAsleep(mMailboxId, ms+(5*SECONDS));
8242033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        }
825ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
826ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Thread.sleep(ms);
827ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (InterruptedException e) {
828ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Doesn't matter whether we stop early; it's the thought that counts
8292033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        } finally {
8302033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            if (runAsleep) {
8312033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                SyncManager.runAwake(mMailboxId);
8322033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            }
833ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
834ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
835ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
8368d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank    private int parsePingResult(InputStream is, ContentResolver cr,
8378d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank            HashMap<String, Integer> errorMap)
838ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        throws IOException, StaleFolderListException {
8398047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        PingParser pp = new PingParser(is, this);
840ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (pp.parse()) {
841ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // True indicates some mailboxes need syncing...
842ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // syncList has the serverId's of the mailboxes...
843ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mBindArguments[0] = Long.toString(mAccount.mId);
8441b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            mPingChangeList = pp.getSyncList();
8451b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            for (String serverId: mPingChangeList) {
846d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                mBindArguments[1] = serverId;
847ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                Cursor c = cr.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
848ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        WHERE_ACCOUNT_KEY_AND_SERVER_ID, mBindArguments, null);
849ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                try {
850ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (c.moveToFirst()) {
8518d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
8528d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        /**
8538d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * Check the boxes reporting changes to see if there really were any...
8548d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * We do this because bugs in various Exchange servers can put us into a
8558d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * looping behavior by continually reporting changes in a mailbox, even when
8568d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * there aren't any.
8578d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         *
8588d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * This behavior is seemingly random, and therefore we must code defensively
8598d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * by backing off of push behavior when it is detected.
8608d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         *
8618d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * One known cause, on certain Exchange 2003 servers, is acknowledged by
8628d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * Microsoft, and the server hotfix for this case can be found at
8638d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * http://support.microsoft.com/kb/923282
8648d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         */
8658d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
8668d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        // Check the status of the last sync
8678d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        String status = c.getString(Mailbox.CONTENT_SYNC_STATUS_COLUMN);
8688d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        int type = SyncManager.getStatusType(status);
8698d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        // This check should always be true...
8708d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        if (type == SyncManager.SYNC_PING) {
8718d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            int changeCount = SyncManager.getStatusChangeCount(status);
8728d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            if (changeCount > 0) {
8738d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                errorMap.remove(serverId);
8748d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            } else if (changeCount == 0) {
8758d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                // This means that a ping reported changes in error; we keep a count
8768d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                // of consecutive errors of this kind
8778d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                String name = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
8788d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                Integer failures = errorMap.get(serverId);
8798d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                if (failures == null) {
8808d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    userLog("Last ping reported changes in error for: ", name);
8818d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    errorMap.put(serverId, 1);
8828d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                } else if (failures > MAX_PING_FAILURES) {
8838d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    // We'll back off of push for this box
8848d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    pushFallback(c.getLong(Mailbox.CONTENT_ID_COLUMN));
8858d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    continue;
8868d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                } else {
8878d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    userLog("Last ping reported changes in error for: ", name);
8888d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    errorMap.put(serverId, failures + 1);
8898d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                }
8908d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            }
8918d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        }
8928d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
8938d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        // If there were no problems with previous sync, we'll start another one
8944d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                        SyncManager.startManualSync(c.getLong(Mailbox.CONTENT_ID_COLUMN),
895ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                SyncManager.SYNC_PING, null);
896ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
897ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } finally {
898ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    c.close();
899ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
900ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
901ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
9021b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        return pp.getSyncStatus();
903ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
904ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
905368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    private String getFilterType() {
906368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        String filter = Eas.FILTER_1_WEEK;
907368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        switch (mAccount.mSyncLookback) {
908368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_DAY: {
909368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_DAY;
910368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
911368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
912368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_3_DAYS: {
913368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_3_DAYS;
914368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
915368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
916368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_WEEK: {
917368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_WEEK;
918368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
919368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
920368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_2_WEEKS: {
921368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_2_WEEKS;
922368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
923368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
924368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_MONTH: {
925368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_MONTH;
926368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
927368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
928368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_ALL: {
929368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_ALL;
930368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
931368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
932368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        }
933368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        return filter;
934368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
935368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
936ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /**
937ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * Common code to sync E+PIM data
938ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     *
939ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @param target, an EasMailbox, EasContacts, or EasCalendar object
940ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
9417c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank    public void sync(AbstractSyncAdapter target) throws IOException {
942ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        Mailbox mailbox = target.mMailbox;
943ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
944ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        boolean moreAvailable = true;
945ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        while (!mStop && moreAvailable) {
9461b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            // If we have no connectivity, just exit cleanly.  SyncManager will start us up again
9471b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            // when connectivity has returned
9481b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            if (!hasConnectivity()) {
9491b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                userLog("No connectivity in sync; finishing sync");
9501b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                mExitStatus = EXIT_DONE;
9511b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                return;
9521b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            }
953ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
954d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            while (true) {
955d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                PartRequest req = null;
956d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                synchronized (mPartRequests) {
957d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    if (mPartRequests.isEmpty()) {
958d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        break;
959d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    } else {
960d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        req = mPartRequests.get(0);
961d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    }
962d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
963842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                getAttachment(req);
964d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                synchronized(mPartRequests) {
965d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    mPartRequests.remove(req);
966d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
967d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            }
968d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
9697c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            Serializer s = new Serializer();
970ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String className = target.getCollectionName();
97148af7392c82262d17700e3fbdccf3a582809d449Marc Blank            String syncKey = target.getSyncKey();
9728d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank            userLog("sync, sending ", className, " syncKey: ", syncKey);
9737c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.start(Tags.SYNC_SYNC)
9747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .start(Tags.SYNC_COLLECTIONS)
9757c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .start(Tags.SYNC_COLLECTION)
9767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_CLASS, className)
97748af7392c82262d17700e3fbdccf3a582809d449Marc Blank                .data(Tags.SYNC_SYNC_KEY, syncKey)
9787c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId)
9797c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .tag(Tags.SYNC_DELETES_AS_MOVES);
980ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
981ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // EAS doesn't like GetChanges if the syncKey is "0"; not documented
98248af7392c82262d17700e3fbdccf3a582809d449Marc Blank            if (!syncKey.equals("0")) {
9837c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.tag(Tags.SYNC_GET_CHANGES);
984ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
9851b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            s.data(Tags.SYNC_WINDOW_SIZE,
9861b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                    className.equals("Email") ? EMAIL_WINDOW_SIZE : PIM_WINDOW_SIZE);
987ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            boolean options = false;
988ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (!className.equals("Contacts")) {
989ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Set the lookback appropriately (EAS calls this a "filter")
990368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                s.start(Tags.SYNC_OPTIONS).data(Tags.SYNC_FILTER_TYPE, getFilterType());
99100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                options = true;
992ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
99300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            if (mProtocolVersionDouble >= 12.0) {
994ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (!options) {
995ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    options = true;
9967c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    s.start(Tags.SYNC_OPTIONS);
997ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
9987c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.start(Tags.BASE_BODY_PREFERENCE)
999368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                    // HTML for email; plain text for everything else
1000c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                    .data(Tags.BASE_TYPE, (className.equals("Email") ? Eas.BODY_PREFERENCE_HTML
1001368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                            : Eas.BODY_PREFERENCE_TEXT))
10027c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    .end();
1003ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1004ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (options) {
10057c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.end();
1006ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1007ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
1008ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Send our changes up to the server
100948af7392c82262d17700e3fbdccf3a582809d449Marc Blank            target.sendLocalChanges(s);
1010ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
10117c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.end().end().end().done();
10128047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            HttpResponse resp = sendHttpClientPost("Sync", s.toByteArray());
10138047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            int code = resp.getStatusLine().getStatusCode();
10141b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            if (code == HttpStatus.SC_OK) {
10158047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 InputStream is = resp.getEntity().getContent();
1016ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (is != null) {
101748af7392c82262d17700e3fbdccf3a582809d449Marc Blank                    moreAvailable = target.parse(is);
101848af7392c82262d17700e3fbdccf3a582809d449Marc Blank                    target.cleanup();
10198d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                } else {
10208d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    userLog("Empty input stream in sync command response");
1021ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
1022ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
10230a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                userLog("Sync response error: ", code);
1024368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                if (isAuthError(code)) {
10251b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    mExitStatus = EXIT_LOGIN_FAILURE;
10261b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                } else {
10271b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    mExitStatus = EXIT_IO_ERROR;
1028ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
1029ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return;
1030ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1031ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
10321b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        mExitStatus = EXIT_DONE;
1033ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
1034ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
10357ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank    protected void setupService() {
10367ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        // Make sure account and mailbox are always the latest from the database
10377ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
10387ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId);
10397ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank
1040ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mThread = Thread.currentThread();
10417310cbacf2cf614c949330faff3882082054c120Marc Blank        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
1042ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        TAG = mThread.getName();
10439d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
1044ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HostAuth ha = HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
1045ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mHostAddress = ha.mAddress;
1046ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mUserName = ha.mLogin;
1047ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mPassword = ha.mPassword;
10487ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank    }
10497ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank
10507ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank    /* (non-Javadoc)
10517ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank     * @see java.lang.Runnable#run()
10527ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank     */
10537ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank    public void run() {
10547ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        setupService();
1055ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
1056fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        try {
1057fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().syncMailboxStatus(mMailboxId, EmailServiceStatus.IN_PROGRESS, 0);
1058fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e1) {
1059fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // Don't care if this fails
1060fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        }
1061fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
1062a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank        // Whether or not we're the account mailbox
1063ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
10649d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            mDeviceId = SyncManager.getDeviceId();
10657ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank            if ((mMailbox == null) || (mAccount == null)) {
1066147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank                return;
10677c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            } else if (mMailbox.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
1068a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank                runAccountMailbox();
1069ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
10707c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                AbstractSyncAdapter target;
107100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mProtocolVersion = mAccount.mProtocolVersion;
107200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
10737cf921b830554e52f88a45ca4a290b17d2a1b146Marc Blank                if (mMailbox.mType == Mailbox.TYPE_CONTACTS) {
10747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    target = new ContactsSyncAdapter(mMailbox, this);
10757cf921b830554e52f88a45ca4a290b17d2a1b146Marc Blank                } else {
10767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    target = new EmailSyncAdapter(mMailbox, this);
1077ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
1078ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // We loop here because someone might have put a request in while we were syncing
1079ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // and we've missed that opportunity...
1080ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                do {
1081ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (mRequestTime != 0) {
1082ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        userLog("Looping for user request...");
1083ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mRequestTime = 0;
1084ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
1085ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    sync(target);
1086ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } while (mRequestTime != 0);
1087ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1088ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (IOException e) {
1089f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank            String message = e.getMessage();
10901431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank            userLog("Caught IOException: ", ((message == null) ? "No message" : message));
1091ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mExitStatus = EXIT_IO_ERROR;
1092ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (Exception e) {
10931431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank            userLog("Uncaught exception in EasSyncService", e);
1094ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } finally {
10954d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank            if (!mStop) {
10968d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                userLog("Sync finished");
10974d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                SyncManager.done(this);
10984d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // If this is the account mailbox, wake up SyncManager
10994d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // Because this box has a "push" interval, it will be restarted immediately
11004d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // which will cause the folder list to be reloaded...
11015c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                int status;
11025c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                switch (mExitStatus) {
11035c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_IO_ERROR:
11045c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.CONNECTION_ERROR;
11055c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
11065c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_DONE:
11075c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.SUCCESS;
11085c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
11095c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_LOGIN_FAILURE:
11105c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.LOGIN_FAILED;
11115c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
11125c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    default:
11135c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.REMOTE_EXCEPTION;
111477424af660458104b732bdcb718874b17d0cab3aMarc Blank                        errorLog("Sync ended due to an exception.");
11155c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
11165c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                }
1117c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
11184d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                try {
11194d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    SyncManager.callback().syncMailboxStatus(mMailboxId, status, 0);
11204d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                } catch (RemoteException e1) {
11214d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    // Don't care if this fails
11224d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                }
11235c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank
11248d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                if (mExitStatus == EXIT_DONE) {
11258d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    // Save the sync time and status
11268d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    ContentValues cv = new ContentValues();
11278d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
11288d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    String s = "S" + mSyncReason + ':' + status + ':' + mChangeCount;
11298d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    cv.put(Mailbox.SYNC_STATUS, s);
11308d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI,
11318d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            mMailboxId), cv, null, null);
11328d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                }
11334d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank            } else {
11348d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                userLog("Stopped sync finished.");
1135fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
11369d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
1137c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            // Make sure SyncManager knows about this
1138c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            SyncManager.kick("sync finished");
11399d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank       }
1140ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
1141ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank}
1142