EasSyncService.java revision 22e927cc109f3668ef3cf2fe46feef273c7ba53e
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
1141b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    static private final int PROTOCOL_PING_STATUS_COMPLETED = 1;
11596293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank
116c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    // Fallbacks (in minutes) for ping loop failures
117252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank    static private final int MAX_PING_FAILURES = 1;
118c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int PING_FALLBACK_INBOX = 5;
11927cf341571fac3d8dbe866f503c34fc31e02bf85Marc Blank    static private final int PING_FALLBACK_PIM = 25;
120d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
121ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    // Reasonable default
122ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mProtocolVersion = "2.5";
12300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank    public Double mProtocolVersionDouble;
12485f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank    protected String mDeviceId = null;
1259d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String mDeviceType = "Android";
1261b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private String mAuthString = null;
1271b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private String mCmdString = null;
128ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mHostAddress;
129ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mUserName;
130ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mPassword;
1311b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private boolean mSsl = true;
1325843b85178a359446f81770ed7734604a1b2fa7dMarc Blank    private boolean mTrustSsl = false;
133ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public ContentResolver mContentResolver;
1341b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private String[] mBindArguments = new String[2];
1351b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private ArrayList<String> mPingChangeList;
1361b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private HttpPost mPendingPost = null;
1371b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    // The ping time (in seconds)
1381b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private int mPingHeartbeat = PING_STARTING_HEARTBEAT;
1391b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    // The longest successful ping heartbeat
1401b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private int mPingHighWaterMark = 0;
1411b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    // Whether we've ever lowered the heartbeat
1421b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    private boolean mPingHeartbeatDropped = false;
143b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank    // Whether a POST was aborted due to watchdog timeout
144b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank    private boolean mAborted = false;
145ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
146ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasSyncService(Context _context, Mailbox _mailbox) {
147ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(_context, _mailbox);
148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mContentResolver = _context.getContentResolver();
149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HostAuth ha = HostAuth.restoreHostAuthWithId(_context, mAccount.mHostAuthKeyRecv);
150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0;
151e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        mTrustSsl = (ha.mFlags & HostAuth.FLAG_TRUST_ALL_CERTIFICATES) != 0;
152ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
153ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
154ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    private EasSyncService(String prefix) {
155ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(prefix);
156ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
157ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
158ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasSyncService() {
159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        this("EAS Validation");
160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
161ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void ping() {
1641b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        userLog("Alarm ping received!");
1651b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        synchronized(getSynchronizer()) {
1661b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            if (mPendingPost != null) {
1671b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                userLog("Aborting pending POST!");
168b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                mAborted = true;
1691b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                mPendingPost.abort();
1701b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            }
171ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
172ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
173ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
174ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void stop() {
176ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mStop = true;
177c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        synchronized(getSynchronizer()) {
178c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            if (mPendingPost != null) {
179c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                mPendingPost.abort();
180c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            }
181c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
1825c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank    }
183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
1841b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    /**
1851b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * Determine whether an HTTP code represents an authentication error
1861b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * @param code the HTTP code returned by the server
1871b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     * @return whether or not the code represents an authentication error
1881b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank     */
189f3ae2f9ee2ced1afc5cac4ebad125161726b6c0bMarc Blank    protected boolean isAuthError(int code) {
1907ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        return ((code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN));
191368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
192368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
193fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank    @Override
194ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void validateAccount(String hostAddress, String userName, String password, int port,
195e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            boolean ssl, boolean trustCertificates, Context context) throws MessagingException {
196ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
1970a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank            userLog("Testing EAS: ", hostAddress, ", ", userName, ", ssl = ", ssl ? "1" : "0");
198ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            EasSyncService svc = new EasSyncService("%TestAccount%");
1999d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            svc.mContext = context;
200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mHostAddress = hostAddress;
201ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mUserName = userName;
202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mPassword = password;
203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mSsl = ssl;
204e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            svc.mTrustSsl = trustCertificates;
2059d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            svc.mDeviceId = SyncManager.getDeviceId();
2068047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            HttpResponse resp = svc.sendHttpClientOptions();
2078047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            int code = resp.getStatusLine().getStatusCode();
2089d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            userLog("Validation (OPTIONS) response: " + code);
2091b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            if (code == HttpStatus.SC_OK) {
210ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // No exception means successful validation
2113b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank                Header commands = resp.getFirstHeader("MS-ASProtocolCommands");
2123b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank                Header versions = resp.getFirstHeader("ms-asprotocolversions");
2133b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank                if (commands == null || versions == null) {
2143b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank                    userLog("OPTIONS response without commands or versions; reporting I/O error");
2153b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank                    throw new MessagingException(MessagingException.IOERROR);
2163b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank                }
217ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Validation successful");
218ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return;
219ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
220368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            if (isAuthError(code)) {
221ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Authentication failed");
222ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                throw new AuthenticationFailedException("Validation failed");
223ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
224ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // TODO Need to catch other kinds of errors (e.g. policy) For now, report the code.
2250a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                userLog("Validation failed, reporting I/O error: ", code);
226ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                throw new MessagingException(MessagingException.IOERROR);
227ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
228ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (IOException e) {
229e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            Throwable cause = e.getCause();
230e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            if (cause != null && cause instanceof CertificateException) {
231e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank                userLog("CertificateException caught: ", e.getMessage());
232e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank                throw new MessagingException(MessagingException.GENERAL_SECURITY);
233e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            }
234e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank            userLog("IOException caught: ", e.getMessage());
235ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            throw new MessagingException(MessagingException.IOERROR);
236ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
237ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
238ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
239ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
24081d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank    private void doStatusCallback(long messageId, long attachmentId, int status) {
241d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        try {
242fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().loadAttachmentStatus(messageId, attachmentId, status, 0);
243fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e) {
244d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            // No danger if the client is no longer around
245d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        }
246d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    }
247ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
24881d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank    private void doProgressCallback(long messageId, long attachmentId, int progress) {
249d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        try {
250fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().loadAttachmentStatus(messageId, attachmentId,
251fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                    EmailServiceStatus.IN_PROGRESS, progress);
252fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e) {
253d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            // No danger if the client is no longer around
254d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        }
255d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    }
256d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
257842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    public File createUniqueFileInternal(String dir, String filename) {
258842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        File directory;
259842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (dir == null) {
260842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory = mContext.getFilesDir();
261842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        } else {
262842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory = new File(dir);
263842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
264842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (!directory.exists()) {
265842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory.mkdirs();
266842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
267842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        File file = new File(directory, filename);
268842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (!file.exists()) {
269842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            return file;
270842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
271842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        // Get the extension of the file, if any.
272842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        int index = filename.lastIndexOf('.');
273842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        String name = filename;
274842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        String extension = "";
275842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (index != -1) {
276842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            name = filename.substring(0, index);
277842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            extension = filename.substring(index);
278842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
279842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        for (int i = 2; i < Integer.MAX_VALUE; i++) {
280842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            file = new File(directory, name + '-' + i + extension);
281842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            if (!file.exists()) {
282842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                return file;
283842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            }
284842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
285842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        return null;
286842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    }
287842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank
288d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    /**
289d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * Loads an attachment, based on the PartRequest passed in.  The PartRequest is basically our
290d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * wrapper for Attachment
291d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * @param req the part (attachment) to be retrieved
292d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * @throws IOException
293d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     */
294842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    protected void getAttachment(PartRequest req) throws IOException {
295d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        Attachment att = req.att;
296b0ce70f8d18dfe14fdd72be528d89eda1ba229feMarc Blank        Message msg = Message.restoreMessageWithId(mContext, att.mMessageKey);
29781d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank        doProgressCallback(msg.mId, att.mId, 0);
2985bd8d6f8310305bce81071ade6ebdb42dbed6b72Marc Blank        HttpClient client = getHttpClient(COMMAND_TIMEOUT);
299d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        String us = makeUriString("GetAttachment", "&AttachmentName=" + att.mLocation);
300ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HttpPost method = new HttpPost(URI.create(us));
301ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        method.setHeader("Authorization", mAuthString);
302ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
303ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HttpResponse res = client.execute(method);
304ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        int status = res.getStatusLine().getStatusCode();
3051b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        if (status == HttpStatus.SC_OK) {
306ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            HttpEntity e = res.getEntity();
307ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int len = (int)e.getContentLength();
308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            InputStream is = res.getEntity().getContent();
309bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler            File f = (req.destination != null)
310bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    ? new File(req.destination)
311bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    : createUniqueFileInternal(req.destination, att.mFileName);
312ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (f != null) {
313bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                // Ensure that the target directory exists
314bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                File destDir = f.getParentFile();
315bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                if (!destDir.exists()) {
316bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    destDir.mkdirs();
317bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                }
318ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                FileOutputStream os = new FileOutputStream(f);
319ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (len > 0) {
320ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    try {
321ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartRequest = req;
322ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        byte[] bytes = new byte[CHUNK_SIZE];
323ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        int length = len;
324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        while (len > 0) {
325ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int n = (len > CHUNK_SIZE ? CHUNK_SIZE : len);
326ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int read = is.read(bytes, 0, n);
327ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            os.write(bytes, 0, read);
328ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            len -= read;
329d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                            int pct = ((length - len) * 100 / length);
33081d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank                            doProgressCallback(msg.mId, att.mId, pct);
331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } finally {
333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartRequest = null;
334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
336ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                os.flush();
337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                os.close();
338ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
339d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                // EmailProvider will throw an exception if we try to update an unsaved attachment
340d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                if (att.isSaved()) {
341bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    String contentUriString = (req.contentUriString != null)
342bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                            ? req.contentUriString
343bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                            : "file://" + f.getAbsolutePath();
344d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    ContentValues cv = new ContentValues();
345bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    cv.put(AttachmentColumns.CONTENT_URI, contentUriString);
346d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    att.update(mContext, cv);
34781d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank                    doStatusCallback(msg.mId, att.mId, EmailServiceStatus.SUCCESS);
348d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
350d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        } else {
35181d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank            doStatusCallback(msg.mId, att.mId, EmailServiceStatus.MESSAGE_NOT_FOUND);
352ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
353ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
354ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
355147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank    @SuppressWarnings("deprecation")
3569d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String makeUriString(String cmd, String extra) throws IOException {
357ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank         // Cache the authentication string and the command string
358ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String safeUserName = URLEncoder.encode(mUserName);
359ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (mAuthString == null) {
360ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String cs = mUserName + ':' + mPassword;
3611b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            mAuthString = "Basic " + new String(Base64.encodeBase64(cs.getBytes()));
362ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId + "&DeviceType="
363ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    + mDeviceType;
364ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
365e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        String us = (mSsl ? (mTrustSsl ? "httpts" : "https") : "http") + "://" + mHostAddress +
366ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            "/Microsoft-Server-ActiveSync";
367ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (cmd != null) {
368ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            us += "?Cmd=" + cmd + mCmdString;
369ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
370ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (extra != null) {
371ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            us += extra;
372ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
373ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return us;
374ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
375ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3768047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private void setHeaders(HttpRequestBase method) {
3778047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("Authorization", mAuthString);
3788047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("MS-ASProtocolVersion", mProtocolVersion);
3798047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("Connection", "keep-alive");
3808047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("User-Agent", mDeviceType + '/' + Eas.VERSION);
3818047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
382ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
383e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank    private ClientConnectionManager getClientConnectionManager() {
384e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        return SyncManager.getClientConnectionManager();
385e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank    }
386e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank
3878047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private HttpClient getHttpClient(int timeout) {
3888047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpParams params = new BasicHttpParams();
3891b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        HttpConnectionParams.setConnectionTimeout(params, 15*SECONDS);
3908047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpConnectionParams.setSoTimeout(params, timeout);
391e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        HttpConnectionParams.setSocketBufferSize(params, 8192);
392e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        HttpClient client = new DefaultHttpClient(getClientConnectionManager(), params);
393e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank        return client;
3948047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
395ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3968047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    protected HttpResponse sendHttpClientPost(String cmd, byte[] bytes) throws IOException {
3971b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        return sendHttpClientPost(cmd, new ByteArrayEntity(bytes), COMMAND_TIMEOUT);
3989e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank    }
3999e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank
4009e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank    protected HttpResponse sendHttpClientPost(String cmd, HttpEntity entity) throws IOException {
4011b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        return sendHttpClientPost(cmd, entity, COMMAND_TIMEOUT);
4021b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    }
4031b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
4042033dfc4e2e6e352b34565112266084d72c443f1Marc Blank    protected HttpResponse sendPing(byte[] bytes, int heartbeat) throws IOException {
4051b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank       Thread.currentThread().setName(mAccount.mDisplayName + ": Ping");
4061b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank       if (Eas.USER_LOG) {
4072033dfc4e2e6e352b34565112266084d72c443f1Marc Blank           userLog("Send ping, timeout: " + heartbeat + "s, high: " + mPingHighWaterMark + 's');
4081b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank       }
4092033dfc4e2e6e352b34565112266084d72c443f1Marc Blank       return sendHttpClientPost(PING_COMMAND, new ByteArrayEntity(bytes), (heartbeat+5)*SECONDS);
4101b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    }
4111b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
4121b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank    protected HttpResponse sendHttpClientPost(String cmd, HttpEntity entity, int timeout)
4131b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            throws IOException {
4141b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        HttpClient client = getHttpClient(timeout);
415940d64c988589390353875bf1351818bd628cfb7Marc Blank        boolean sleepAllowed = cmd.equals(PING_COMMAND);
41685f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank
41785f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        // Split the mail sending commands
41885f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        String extra = null;
41985f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        boolean msg = false;
42085f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) {
4215843b85178a359446f81770ed7734604a1b2fa7dMarc Blank            int cmdLength = cmd.indexOf('&');
42285f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank            extra = cmd.substring(cmdLength);
42385f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank            cmd = cmd.substring(0, cmdLength);
42485f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank            msg = true;
42585f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        } else if (cmd.startsWith("SendMail&")) {
42685f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank            msg = true;
42785f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        }
42885f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank
42985f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        String us = makeUriString(cmd, extra);
4308047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpPost method = new HttpPost(URI.create(us));
43185f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank        if (msg) {
4328047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            method.setHeader("Content-Type", "message/rfc822");
4338047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        } else {
4348047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml");
435ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
4368047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        setHeaders(method);
4379e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank        method.setEntity(entity);
438c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        synchronized(getSynchronizer()) {
439c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            mPendingPost = method;
440940d64c988589390353875bf1351818bd628cfb7Marc Blank            if (sleepAllowed) {
441940d64c988589390353875bf1351818bd628cfb7Marc Blank                SyncManager.runAsleep(mMailboxId, timeout+(10*SECONDS));
442940d64c988589390353875bf1351818bd628cfb7Marc Blank            }
443c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
444c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        try {
445c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            return client.execute(method);
446c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        } finally {
447c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            synchronized(getSynchronizer()) {
448940d64c988589390353875bf1351818bd628cfb7Marc Blank                if (sleepAllowed) {
449940d64c988589390353875bf1351818bd628cfb7Marc Blank                    SyncManager.runAwake(mMailboxId);
450940d64c988589390353875bf1351818bd628cfb7Marc Blank                }
451c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                mPendingPost = null;
452c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            }
453c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
4548047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
4558047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
4568047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    protected HttpResponse sendHttpClientOptions() throws IOException {
4578047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpClient client = getHttpClient(COMMAND_TIMEOUT);
4588047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        String us = makeUriString("OPTIONS", null);
4598047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpOptions method = new HttpOptions(URI.create(us));
4608047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        setHeaders(method);
4618047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        return client.execute(method);
462ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
463ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
464ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String getTargetCollectionClassFromCursor(Cursor c) {
465ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
466ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (type == Mailbox.TYPE_CONTACTS) {
467ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Contacts";
468ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else if (type == Mailbox.TYPE_CALENDAR) {
469ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Calendar";
470ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else {
471ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Email";
472ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
473ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
474ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
475ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /**
476ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * Performs FolderSync
477ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     *
478ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @throws IOException
479ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @throws EasParserException
480ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
481a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank    public void runAccountMailbox() throws IOException, EasParserException {
482fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        // Initialize exit status to success
483fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        mExitStatus = EmailServiceStatus.SUCCESS;
484ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
485fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            try {
486fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                SyncManager.callback()
487fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                    .syncMailboxListStatus(mAccount.mId, EmailServiceStatus.IN_PROGRESS, 0);
488fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            } catch (RemoteException e1) {
489fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                // Don't care if this fails
490fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
491fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
492ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mAccount.mSyncKey == null) {
493ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mAccount.mSyncKey = "0";
4941b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Account syncKey INIT to 0");
4959387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                ContentValues cv = new ContentValues();
4969387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
4979387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                mAccount.update(mContext, cv);
498ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
499ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
5009d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            boolean firstSync = mAccount.mSyncKey.equals("0");
5019d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            if (firstSync) {
5021b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Initial FolderSync");
5031b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            }
5041b275b9408d5b856e2482fa3951827489e9585ccMarc Blank
5054626078bf9d930b2007162db142b5961b38e2166Marc Blank            // When we first start up, change all mailboxes to push.
506ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ContentValues cv = new ContentValues();
507f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank            cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
508ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
5099d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING,
5109d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    new String[] {Long.toString(mAccount.mId)}) > 0) {
5119d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                SyncManager.kick("change ping boxes to push");
512ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
513ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
5140f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            // Determine our protocol version, if we haven't already
5150f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            if (mAccount.mProtocolVersion == null) {
5161b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Determine EAS protocol version");
5178047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                HttpResponse resp = sendHttpClientOptions();
5188047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                int code = resp.getStatusLine().getStatusCode();
5190a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                userLog("OPTIONS response: ", code);
5201b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                if (code == HttpStatus.SC_OK) {
521647aa5f1f229947d982548f698c4533fe538f884Marc Blank                    Header header = resp.getFirstHeader("MS-ASProtocolCommands");
522647aa5f1f229947d982548f698c4533fe538f884Marc Blank                    userLog(header.getValue());
523647aa5f1f229947d982548f698c4533fe538f884Marc Blank                    header = resp.getFirstHeader("ms-asprotocolversions");
5248047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    String versions = header.getValue();
5258047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    if (versions != null) {
5268047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        if (versions.contains("12.0")) {
5278047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                            mProtocolVersion = "12.0";
528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
5298047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
5308047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        mAccount.mProtocolVersion = mProtocolVersion;
5318047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        userLog(versions);
5320a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                        userLog("Using version ", mProtocolVersion);
533ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } else {
5348047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        errorLog("No protocol versions in OPTIONS response");
535ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        throw new IOException();
536ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
5378047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                } else {
5388047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    errorLog("OPTIONS command failed; throwing IOException");
5398047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    throw new IOException();
5400f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
5410f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            }
5421b275b9408d5b856e2482fa3951827489e9585ccMarc Blank
54377424af660458104b732bdcb718874b17d0cab3aMarc Blank            // Change all pushable boxes to push when we start the account mailbox
54477424af660458104b732bdcb718874b17d0cab3aMarc Blank            if (mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH) {
54577424af660458104b732bdcb718874b17d0cab3aMarc Blank                cv = new ContentValues();
546f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank                cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
54777424af660458104b732bdcb718874b17d0cab3aMarc Blank                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
54877424af660458104b732bdcb718874b17d0cab3aMarc Blank                        SyncManager.WHERE_IN_ACCOUNT_AND_PUSHABLE,
54977424af660458104b732bdcb718874b17d0cab3aMarc Blank                        new String[] {Long.toString(mAccount.mId)}) > 0) {
55077424af660458104b732bdcb718874b17d0cab3aMarc Blank                    userLog("Push account; set pushable boxes to push...");
55177424af660458104b732bdcb718874b17d0cab3aMarc Blank                }
55277424af660458104b732bdcb718874b17d0cab3aMarc Blank            }
55377424af660458104b732bdcb718874b17d0cab3aMarc Blank
55477424af660458104b732bdcb718874b17d0cab3aMarc Blank            while (!mStop) {
5550a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                 userLog("Sending Account syncKey: ", mAccount.mSyncKey);
5568047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 Serializer s = new Serializer();
5578047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
55877424af660458104b732bdcb718874b17d0cab3aMarc Blank                     .text(mAccount.mSyncKey).end().end().done();
5598047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 HttpResponse resp = sendHttpClientPost("FolderSync", s.toByteArray());
5608047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 if (mStop) break;
5618047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 int code = resp.getStatusLine().getStatusCode();
5621b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                 if (code == HttpStatus.SC_OK) {
5638047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     HttpEntity entity = resp.getEntity();
5648047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     int len = (int)entity.getContentLength();
5658047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     if (len > 0) {
5668047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         InputStream is = entity.getContent();
5678047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         // Returns true if we need to sync again
56848af7392c82262d17700e3fbdccf3a582809d449Marc Blank                         if (new FolderSyncParser(is, new AccountSyncAdapter(mMailbox, this))
56948af7392c82262d17700e3fbdccf3a582809d449Marc Blank                                 .parse()) {
5708047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                             continue;
5718047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         }
5728047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     }
5731b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                 } else if (isAuthError(code)) {
5741b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    mExitStatus = EXIT_LOGIN_FAILURE;
5750f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } else {
5760a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                    userLog("FolderSync response error: ", code);
5770f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
578ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
5799d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                // Change all push/hold boxes to push
5809d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                cv = new ContentValues();
5819d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
5829d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
5839d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX,
5849d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        new String[] {Long.toString(mAccount.mId)}) > 0) {
5859d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    userLog("Set push/hold boxes to push...");
5869d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                }
5879d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
5880f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                try {
5890f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    SyncManager.callback()
5909d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        .syncMailboxListStatus(mAccount.mId, mExitStatus, 0);
5910f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } catch (RemoteException e1) {
5920f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    // Don't care if this fails
5930f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
594fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
5950f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                // Wait for push notifications.
5960f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                String threadName = Thread.currentThread().getName();
5970f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                try {
5980f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    runPingLoop();
5990f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } catch (StaleFolderListException e) {
6000f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    // We break out if we get told about a stale folder list
6010f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    userLog("Ping interrupted; folder list requires sync...");
6020f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } finally {
6030f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    Thread.currentThread().setName(threadName);
6040f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
605ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
6060f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank         } catch (IOException e) {
607fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // We catch this here to send the folder sync status callback
608fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // A folder sync failed callback will get sent from run()
609fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            try {
6104d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                if (!mStop) {
6114d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    SyncManager.callback()
6124d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                        .syncMailboxListStatus(mAccount.mId,
6134d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                                EmailServiceStatus.CONNECTION_ERROR, 0);
6144d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                }
615fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            } catch (RemoteException e1) {
616fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                // Don't care if this fails
617fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
61896293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank            throw e;
619ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
620ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
621ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
622c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    void pushFallback(long mailboxId) {
623c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
624c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        ContentValues cv = new ContentValues();
6254626078bf9d930b2007162db142b5961b38e2166Marc Blank        int mins = PING_FALLBACK_PIM;
626c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        if (mailbox.mType == Mailbox.TYPE_INBOX) {
6274626078bf9d930b2007162db142b5961b38e2166Marc Blank            mins = PING_FALLBACK_INBOX;
628c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
6294626078bf9d930b2007162db142b5961b38e2166Marc Blank        cv.put(Mailbox.SYNC_INTERVAL, mins);
6304626078bf9d930b2007162db142b5961b38e2166Marc Blank        mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
6314626078bf9d930b2007162db142b5961b38e2166Marc Blank                cv, null, null);
63227cf341571fac3d8dbe866f503c34fc31e02bf85Marc Blank        errorLog("*** PING ERROR LOOP: Set " + mailbox.mDisplayName + " to " + mins + " min sync");
6334626078bf9d930b2007162db142b5961b38e2166Marc Blank        SyncManager.kick("push fallback");
634368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
635368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
636ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    void runPingLoop() throws IOException, StaleFolderListException {
6371b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        int pingHeartbeat = mPingHeartbeat;
63874c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank        userLog("runPingLoop");
639ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // Do push for all sync services here
640c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        long endTime = System.currentTimeMillis() + (30*MINUTES);
6411b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        HashMap<String, Integer> pingErrorMap = new HashMap<String, Integer>();
6422033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        ArrayList<String> readyMailboxes = new ArrayList<String>();
6432033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        ArrayList<String> notReadyMailboxes = new ArrayList<String>();
6447672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank        int pingWaitCount = 0;
6458d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
6467ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        while ((System.currentTimeMillis() < endTime) && !mStop) {
647ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Count of pushable mailboxes
648ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int pushCount = 0;
649ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Count of mailboxes that can be pushed right now
650ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int canPushCount = 0;
6518a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank            // Count of uninitialized boxes
6528a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank            int uninitCount = 0;
6538a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank
6547c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            Serializer s = new Serializer();
655ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
65622bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                    MailboxColumns.ACCOUNT_KEY + '=' + mAccount.mId +
65722bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                    AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX, null, null);
6582033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            notReadyMailboxes.clear();
6592033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            readyMailboxes.clear();
660ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            try {
661ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Loop through our pushed boxes seeing what is available to push
662ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                while (c.moveToNext()) {
663ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    pushCount++;
664ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // Two requirements for push:
665ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // 1) SyncManager tells us the mailbox is syncable (not running, not stopped)
666ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // 2) The syncKey isn't "0" (i.e. it's synced at least once)
667ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
66877424af660458104b732bdcb718874b17d0cab3aMarc Blank                    int pingStatus = SyncManager.pingStatus(mailboxId);
66977424af660458104b732bdcb718874b17d0cab3aMarc Blank                    String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
67077424af660458104b732bdcb718874b17d0cab3aMarc Blank                    if (pingStatus == SyncManager.PING_STATUS_OK) {
671a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank
672ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN);
6737ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                        if ((syncKey == null) || syncKey.equals("0")) {
674a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank                            // We can't push until the initial sync is done
67577424af660458104b732bdcb718874b17d0cab3aMarc Blank                            pushCount--;
6768a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank                            uninitCount++;
677ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            continue;
678ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
679ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank
680ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        if (canPushCount++ == 0) {
681ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            // Initialize the Ping command
68296293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank                            s.start(Tags.PING_PING)
68305381a6662f28609e8005023515abb82af00e1d4Marc Blank                                .data(Tags.PING_HEARTBEAT_INTERVAL,
6841b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                                        Integer.toString(pingHeartbeat))
6857c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                                .start(Tags.PING_FOLDERS);
686ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
6871b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
688ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String folderClass = getTargetCollectionClassFromCursor(c);
6897c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                        s.start(Tags.PING_FOLDER)
6907c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .data(Tags.PING_ID, c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN))
6917c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .data(Tags.PING_CLASS, folderClass)
6927c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .end();
6932033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                        readyMailboxes.add(mailboxName);
6947ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                    } else if ((pingStatus == SyncManager.PING_STATUS_RUNNING) ||
6957ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                            (pingStatus == SyncManager.PING_STATUS_WAITING)) {
6962033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                        notReadyMailboxes.add(mailboxName);
69777424af660458104b732bdcb718874b17d0cab3aMarc Blank                    } else if (pingStatus == SyncManager.PING_STATUS_UNABLE) {
69877424af660458104b732bdcb718874b17d0cab3aMarc Blank                        pushCount--;
6990a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                        userLog(mailboxName, " in error state; ignore");
70077424af660458104b732bdcb718874b17d0cab3aMarc Blank                        continue;
701ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
702ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
703ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } finally {
704ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                c.close();
705ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
706ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
7072033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            if (Eas.USER_LOG) {
7082033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                if (!notReadyMailboxes.isEmpty()) {
7092033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                    userLog("Ping not ready for: " + notReadyMailboxes);
7102033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                }
7112033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                if (!readyMailboxes.isEmpty()) {
7122033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                    userLog("Ping ready for: " + readyMailboxes);
7132033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                }
7142033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            }
7152033dfc4e2e6e352b34565112266084d72c443f1Marc Blank
7167672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank            // If we've waited 10 seconds or more, just ping with whatever boxes are ready
7177672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank            // But use a shorter than normal heartbeat
7182033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            boolean forcePing = !notReadyMailboxes.isEmpty() && (pingWaitCount > 5);
7197672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank
7207672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank            if ((canPushCount > 0) && ((canPushCount == pushCount) || forcePing)) {
7217672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                // If all pingable boxes are ready for push, send Ping to the server
7227c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.end().end().done();
7237672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                pingWaitCount = 0;
7247c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank
7251b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                // If we've been stopped, this is a good time to return
7264d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                if (mStop) return;
727368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
72874c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank                long pingTime = SystemClock.elapsedRealtime();
7291b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                try {
7301b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    // Send the ping, wrapped by appropriate timeout/alarm
7317672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                    if (forcePing) {
7327672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                        userLog("Forcing ping after waiting for all boxes to be ready");
7337672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                    }
7347672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                    HttpResponse res =
7357672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                        sendPing(s.toByteArray(), forcePing ? PING_FORCE_HEARTBEAT : pingHeartbeat);
736c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
7371b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    int code = res.getStatusLine().getStatusCode();
7381b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    userLog("Ping response: ", code);
739368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
7401b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    // Return immediately if we've been asked to stop during the ping
7411b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    if (mStop) {
7421b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        userLog("Stopping pingLoop");
7431b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        return;
7441b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    }
7451b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank
7461b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    if (code == HttpStatus.SC_OK) {
7471b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        HttpEntity e = res.getEntity();
7481b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        int len = (int)e.getContentLength();
7491b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        InputStream is = res.getEntity().getContent();
7501b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        if (len > 0) {
7518d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            int pingResult = parsePingResult(is, mContentResolver, pingErrorMap);
7527672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                            // If our ping completed (status = 1), and we weren't forced and we're
7537672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                            // not at the maximum, try increasing timeout by two minutes
7548d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            if (pingResult == PROTOCOL_PING_STATUS_COMPLETED && !forcePing) {
7557672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                if (pingHeartbeat > mPingHighWaterMark) {
7567672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    mPingHighWaterMark = pingHeartbeat;
7577672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    userLog("Setting high water mark at: ", mPingHighWaterMark);
7587672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                }
7597672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                if ((pingHeartbeat < PING_MAX_HEARTBEAT) &&
7607672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                        !mPingHeartbeatDropped) {
7617672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    pingHeartbeat += PING_HEARTBEAT_INCREMENT;
7627672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    if (pingHeartbeat > PING_MAX_HEARTBEAT) {
7637672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                        pingHeartbeat = PING_MAX_HEARTBEAT;
7647672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    }
7657672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                                    userLog("Increasing ping heartbeat to ", pingHeartbeat, "s");
7661b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                                }
7671b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            }
7681b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        } else {
7691b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            userLog("Ping returned empty result; throwing IOException");
7701b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            throw new IOException();
7711b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        }
7721b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    } else if (isAuthError(code)) {
7731b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        mExitStatus = EXIT_LOGIN_FAILURE;
7741b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        userLog("Authorization error during Ping: ", code);
775ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        throw new IOException();
776ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
7771b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                } catch (IOException e) {
778252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank                    String message = e.getMessage();
7791b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    // If we get the exception that is indicative of a NAT timeout and if we
7801b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    // haven't yet "fixed" the timeout, back off by two minutes and "fix" it
781252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank                    boolean hasMessage = message != null;
782252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank                    userLog("IOException runPingLoop: " + (hasMessage ? message : "[no message]"));
783b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                    if (mAborted || (hasMessage && message.contains("reset by peer"))) {
784b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                        long pingLength = SystemClock.elapsedRealtime() - pingTime;
7857ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                        if ((pingHeartbeat > PING_MIN_HEARTBEAT) &&
7867ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank                                (pingHeartbeat > mPingHighWaterMark)) {
7871b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            pingHeartbeat -= PING_HEARTBEAT_INCREMENT;
7881b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            mPingHeartbeatDropped = true;
7891b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            if (pingHeartbeat < PING_MIN_HEARTBEAT) {
7901b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                                pingHeartbeat = PING_MIN_HEARTBEAT;
7911b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            }
7921b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                            userLog("Decreased ping heartbeat to ", pingHeartbeat, "s");
793b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                        } else if (mAborted || (pingLength < 2000)) {
794b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank                            userLog("Abort or NAT type return < 2 seconds; throwing IOException");
79574c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank                            throw e;
79674c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank                        } else {
79774c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank                            userLog("NAT type IOException > 2 seconds?");
7981b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        }
7991b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    } else {
8001b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                        throw e;
8011b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    }
802ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
8032033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            } else if (forcePing) {
8042033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                // In this case, there aren't any boxes that are pingable, but there are boxes
8052033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                // waiting (for IOExceptions)
8062033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                userLog("pingLoop waiting 60s for any pingable boxes");
8072033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                sleep(60*SECONDS, true);
808ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else if (pushCount > 0) {
8091ec0390ce7c27890a46f877d858f7ac6b6a8920cMarc Blank                // If we want to Ping, but can't just yet, wait a little bit
8101ec0390ce7c27890a46f877d858f7ac6b6a8920cMarc Blank                // TODO Change sleep to wait and use notify from SyncManager when a sync ends
8112033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                sleep(2*SECONDS, false);
8127672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank                pingWaitCount++;
8132033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                //userLog("pingLoop waited 2s for: ", (pushCount - canPushCount), " box(es)");
8148a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank            } else if (uninitCount > 0) {
8158a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank                // In this case, we're doing an initial sync of at least one mailbox.  Since this
8168a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank                // is typically a one-time case, I'm ok with trying again every 10 seconds until
8178a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank                // we're in one of the other possible states.
8182033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                userLog("pingLoop waiting for initial sync of ", uninitCount, " box(es)");
8192033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                sleep(10*SECONDS, true);
820ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
821c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                // We've got nothing to do, so we'll check again in 30 minutes at which time
8221b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                // we'll update the folder list.  Let the device sleep in the meantime...
8232033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                userLog("pingLoop sleeping for 30m");
8242033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                sleep(30*MINUTES, true);
825ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
826ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
827ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
828ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
8292033dfc4e2e6e352b34565112266084d72c443f1Marc Blank    void sleep(long ms, boolean runAsleep) {
8302033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        if (runAsleep) {
8312033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            SyncManager.runAsleep(mMailboxId, ms+(5*SECONDS));
8322033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        }
833ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
834ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Thread.sleep(ms);
835ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (InterruptedException e) {
836ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Doesn't matter whether we stop early; it's the thought that counts
8372033dfc4e2e6e352b34565112266084d72c443f1Marc Blank        } finally {
8382033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            if (runAsleep) {
8392033dfc4e2e6e352b34565112266084d72c443f1Marc Blank                SyncManager.runAwake(mMailboxId);
8402033dfc4e2e6e352b34565112266084d72c443f1Marc Blank            }
841ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
842ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
843ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
8448d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank    private int parsePingResult(InputStream is, ContentResolver cr,
8458d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank            HashMap<String, Integer> errorMap)
846ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        throws IOException, StaleFolderListException {
8478047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        PingParser pp = new PingParser(is, this);
848ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (pp.parse()) {
849ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // True indicates some mailboxes need syncing...
850ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // syncList has the serverId's of the mailboxes...
851ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mBindArguments[0] = Long.toString(mAccount.mId);
8521b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            mPingChangeList = pp.getSyncList();
8531b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            for (String serverId: mPingChangeList) {
854d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                mBindArguments[1] = serverId;
855ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                Cursor c = cr.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
856ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        WHERE_ACCOUNT_KEY_AND_SERVER_ID, mBindArguments, null);
857ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                try {
858ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (c.moveToFirst()) {
8598d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
8608d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        /**
8618d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * Check the boxes reporting changes to see if there really were any...
8628d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * We do this because bugs in various Exchange servers can put us into a
8638d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * looping behavior by continually reporting changes in a mailbox, even when
8648d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * there aren't any.
8658d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         *
8668d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * This behavior is seemingly random, and therefore we must code defensively
8678d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * by backing off of push behavior when it is detected.
8688d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         *
8698d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * One known cause, on certain Exchange 2003 servers, is acknowledged by
8708d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * Microsoft, and the server hotfix for this case can be found at
8718d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         * http://support.microsoft.com/kb/923282
8728d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                         */
8738d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
8748d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        // Check the status of the last sync
8758d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        String status = c.getString(Mailbox.CONTENT_SYNC_STATUS_COLUMN);
8768d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        int type = SyncManager.getStatusType(status);
8778d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        // This check should always be true...
8788d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        if (type == SyncManager.SYNC_PING) {
8798d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            int changeCount = SyncManager.getStatusChangeCount(status);
8808d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            if (changeCount > 0) {
8818d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                errorMap.remove(serverId);
8828d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            } else if (changeCount == 0) {
8838d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                // This means that a ping reported changes in error; we keep a count
8848d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                // of consecutive errors of this kind
8858d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                String name = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
8868d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                Integer failures = errorMap.get(serverId);
8878d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                if (failures == null) {
8888d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    userLog("Last ping reported changes in error for: ", name);
8898d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    errorMap.put(serverId, 1);
8908d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                } else if (failures > MAX_PING_FAILURES) {
8918d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    // We'll back off of push for this box
8928d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    pushFallback(c.getLong(Mailbox.CONTENT_ID_COLUMN));
8938d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    continue;
8948d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                } else {
8958d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    userLog("Last ping reported changes in error for: ", name);
8968d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                    errorMap.put(serverId, failures + 1);
8978d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                                }
8988d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            }
8998d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        }
9008d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
9018d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        // If there were no problems with previous sync, we'll start another one
9024d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                        SyncManager.startManualSync(c.getLong(Mailbox.CONTENT_ID_COLUMN),
903ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                SyncManager.SYNC_PING, null);
904ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
905ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } finally {
906ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    c.close();
907ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
908ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
909ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
9101b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        return pp.getSyncStatus();
911ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
912ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
913368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    private String getFilterType() {
914368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        String filter = Eas.FILTER_1_WEEK;
915368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        switch (mAccount.mSyncLookback) {
916368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_DAY: {
917368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_DAY;
918368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
919368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
920368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_3_DAYS: {
921368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_3_DAYS;
922368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
923368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
924368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_WEEK: {
925368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_WEEK;
926368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
927368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
928368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_2_WEEKS: {
929368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_2_WEEKS;
930368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
931368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
932368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_MONTH: {
933368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_MONTH;
934368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
935368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
936368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_ALL: {
937368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_ALL;
938368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
939368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
940368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        }
941368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        return filter;
942368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
943368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
944ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /**
945ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * Common code to sync E+PIM data
946ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     *
947ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @param target, an EasMailbox, EasContacts, or EasCalendar object
948ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
9497c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank    public void sync(AbstractSyncAdapter target) throws IOException {
950ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        Mailbox mailbox = target.mMailbox;
951ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
952ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        boolean moreAvailable = true;
953ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        while (!mStop && moreAvailable) {
9541b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            // If we have no connectivity, just exit cleanly.  SyncManager will start us up again
9551b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            // when connectivity has returned
9561b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            if (!hasConnectivity()) {
9571b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                userLog("No connectivity in sync; finishing sync");
9581b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                mExitStatus = EXIT_DONE;
9591b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                return;
9601b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            }
961ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
962d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            while (true) {
963d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                PartRequest req = null;
964d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                synchronized (mPartRequests) {
965d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    if (mPartRequests.isEmpty()) {
966d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        break;
967d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    } else {
968d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        req = mPartRequests.get(0);
969d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    }
970d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
971842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                getAttachment(req);
972d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                synchronized(mPartRequests) {
973d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    mPartRequests.remove(req);
974d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
975d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            }
976d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
9777c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            Serializer s = new Serializer();
978ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String className = target.getCollectionName();
97948af7392c82262d17700e3fbdccf3a582809d449Marc Blank            String syncKey = target.getSyncKey();
9808d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank            userLog("sync, sending ", className, " syncKey: ", syncKey);
9817c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.start(Tags.SYNC_SYNC)
9827c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .start(Tags.SYNC_COLLECTIONS)
9837c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .start(Tags.SYNC_COLLECTION)
9847c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_CLASS, className)
98548af7392c82262d17700e3fbdccf3a582809d449Marc Blank                .data(Tags.SYNC_SYNC_KEY, syncKey)
9867c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId)
9877c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .tag(Tags.SYNC_DELETES_AS_MOVES);
988ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
989ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // EAS doesn't like GetChanges if the syncKey is "0"; not documented
99048af7392c82262d17700e3fbdccf3a582809d449Marc Blank            if (!syncKey.equals("0")) {
9917c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.tag(Tags.SYNC_GET_CHANGES);
992ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
9931b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            s.data(Tags.SYNC_WINDOW_SIZE,
9941b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                    className.equals("Email") ? EMAIL_WINDOW_SIZE : PIM_WINDOW_SIZE);
995895d1e3132622653160516d420231ed366ab411bMarc Blank
996895d1e3132622653160516d420231ed366ab411bMarc Blank            // Handle options
997895d1e3132622653160516d420231ed366ab411bMarc Blank            s.start(Tags.SYNC_OPTIONS);
998895d1e3132622653160516d420231ed366ab411bMarc Blank            // Set the lookback appropriately (EAS calls this a "filter") for all but Contacts
999ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (!className.equals("Contacts")) {
1000895d1e3132622653160516d420231ed366ab411bMarc Blank                s.data(Tags.SYNC_FILTER_TYPE, getFilterType());
1001ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1002895d1e3132622653160516d420231ed366ab411bMarc Blank            // Set the truncation amount for all classes
100300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            if (mProtocolVersionDouble >= 12.0) {
10047c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.start(Tags.BASE_BODY_PREFERENCE)
1005368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                    // HTML for email; plain text for everything else
1006c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                    .data(Tags.BASE_TYPE, (className.equals("Email") ? Eas.BODY_PREFERENCE_HTML
1007895d1e3132622653160516d420231ed366ab411bMarc Blank                        : Eas.BODY_PREFERENCE_TEXT))
1008895d1e3132622653160516d420231ed366ab411bMarc Blank                    .data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE)
10097c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    .end();
1010895d1e3132622653160516d420231ed366ab411bMarc Blank            } else {
1011895d1e3132622653160516d420231ed366ab411bMarc Blank                s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE);
1012ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1013895d1e3132622653160516d420231ed366ab411bMarc Blank            s.end();
1014ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
1015ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Send our changes up to the server
101648af7392c82262d17700e3fbdccf3a582809d449Marc Blank            target.sendLocalChanges(s);
1017ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
10187c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.end().end().end().done();
10198047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            HttpResponse resp = sendHttpClientPost("Sync", s.toByteArray());
10208047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            int code = resp.getStatusLine().getStatusCode();
10211b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank            if (code == HttpStatus.SC_OK) {
10228047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 InputStream is = resp.getEntity().getContent();
1023ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (is != null) {
102448af7392c82262d17700e3fbdccf3a582809d449Marc Blank                    moreAvailable = target.parse(is);
102548af7392c82262d17700e3fbdccf3a582809d449Marc Blank                    target.cleanup();
10268d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                } else {
10278d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    userLog("Empty input stream in sync command response");
1028ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
1029ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
10300a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                userLog("Sync response error: ", code);
1031368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                if (isAuthError(code)) {
10321b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    mExitStatus = EXIT_LOGIN_FAILURE;
10331b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                } else {
10341b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank                    mExitStatus = EXIT_IO_ERROR;
1035ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
1036ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return;
1037ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1038ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
10391b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank        mExitStatus = EXIT_DONE;
1040ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
1041ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
104222e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank    protected boolean setupService() {
10437ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        // Make sure account and mailbox are always the latest from the database
10447ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
104522e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank        if (mAccount == null) return false;
10467ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank        mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId);
104722e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank        if (mMailbox == null) return false;
1048ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mThread = Thread.currentThread();
10497310cbacf2cf614c949330faff3882082054c120Marc Blank        android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
1050ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        TAG = mThread.getName();
10519d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
1052ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HostAuth ha = HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
105322e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank        if (ha == null) return false;
1054ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mHostAddress = ha.mAddress;
1055ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mUserName = ha.mLogin;
1056ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mPassword = ha.mPassword;
105722e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank        return true;
10587ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank    }
10597ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank
10607ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank    /* (non-Javadoc)
10617ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank     * @see java.lang.Runnable#run()
10627ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank     */
10637ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank    public void run() {
106422e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank        if (!setupService()) return;
1065ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
1066fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        try {
1067fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().syncMailboxStatus(mMailboxId, EmailServiceStatus.IN_PROGRESS, 0);
1068fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e1) {
1069fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // Don't care if this fails
1070fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        }
1071fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
1072a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank        // Whether or not we're the account mailbox
1073ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
10749d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            mDeviceId = SyncManager.getDeviceId();
10757ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank            if ((mMailbox == null) || (mAccount == null)) {
1076147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank                return;
10777c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            } else if (mMailbox.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
1078a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank                runAccountMailbox();
1079ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
10807c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                AbstractSyncAdapter target;
108100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mProtocolVersion = mAccount.mProtocolVersion;
108200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
10837cf921b830554e52f88a45ca4a290b17d2a1b146Marc Blank                if (mMailbox.mType == Mailbox.TYPE_CONTACTS) {
10847c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    target = new ContactsSyncAdapter(mMailbox, this);
10857cf921b830554e52f88a45ca4a290b17d2a1b146Marc Blank                } else {
10867c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    target = new EmailSyncAdapter(mMailbox, this);
1087ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
1088ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // We loop here because someone might have put a request in while we were syncing
1089ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // and we've missed that opportunity...
1090ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                do {
1091ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (mRequestTime != 0) {
1092ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        userLog("Looping for user request...");
1093ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mRequestTime = 0;
1094ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
1095ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    sync(target);
1096ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } while (mRequestTime != 0);
1097ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1098ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (IOException e) {
1099f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank            String message = e.getMessage();
11001431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank            userLog("Caught IOException: ", ((message == null) ? "No message" : message));
1101ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mExitStatus = EXIT_IO_ERROR;
1102ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (Exception e) {
11031431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank            userLog("Uncaught exception in EasSyncService", e);
1104ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } finally {
11054d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank            if (!mStop) {
11068d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                userLog("Sync finished");
11074d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                SyncManager.done(this);
11084d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // If this is the account mailbox, wake up SyncManager
11094d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // Because this box has a "push" interval, it will be restarted immediately
11104d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // which will cause the folder list to be reloaded...
11115c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                int status;
11125c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                switch (mExitStatus) {
11135c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_IO_ERROR:
11145c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.CONNECTION_ERROR;
11155c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
11165c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_DONE:
11175c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.SUCCESS;
11185c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
11195c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_LOGIN_FAILURE:
11205c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.LOGIN_FAILED;
11215c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
11225c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    default:
11235c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.REMOTE_EXCEPTION;
112477424af660458104b732bdcb718874b17d0cab3aMarc Blank                        errorLog("Sync ended due to an exception.");
11255c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
11265c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                }
1127c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
11284d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                try {
11294d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    SyncManager.callback().syncMailboxStatus(mMailboxId, status, 0);
11304d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                } catch (RemoteException e1) {
11314d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    // Don't care if this fails
11324d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                }
11335c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank
11348d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                if (mExitStatus == EXIT_DONE) {
11358d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    // Save the sync time and status
11368d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    ContentValues cv = new ContentValues();
11378d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
11388d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    String s = "S" + mSyncReason + ':' + status + ':' + mChangeCount;
11398d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    cv.put(Mailbox.SYNC_STATUS, s);
11408d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI,
11418d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                            mMailboxId), cv, null, null);
11428d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                }
11434d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank            } else {
11448d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                userLog("Stopped sync finished.");
1145fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
11469d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
1147c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            // Make sure SyncManager knows about this
1148c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            SyncManager.kick("sync finished");
11499d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank       }
1150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
1151ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank}
1152