EasSyncService.java revision f4ec9557c58b0c5918e3ae4cde23e1355dc0a2af
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
20ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport com.android.email.R;
21ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport com.android.email.activity.AccountFolderList;
22ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.email.mail.AuthenticationFailedException;
23ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.email.mail.MessagingException;
2467698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Account;
2567698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.AccountColumns;
2667698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Attachment;
2767698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.AttachmentColumns;
2867698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.HostAuth;
2967698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Mailbox;
3067698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.MailboxColumns;
3167698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Message;
327c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.AbstractSyncAdapter;
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 Blankimport com.android.exchange.utility.Base64;
41ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
428047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.Header;
4300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.HttpEntity;
4400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.HttpResponse;
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;
498047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.entity.ByteArrayEntity;
5000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.impl.client.DefaultHttpClient;
518047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.BasicHttpParams;
528047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.HttpConnectionParams;
538047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.HttpParams;
5400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank
55ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport android.app.Notification;
56ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport android.app.NotificationManager;
57ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport android.app.PendingIntent;
58ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentResolver;
59ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport android.content.ContentUris;
60ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentValues;
61ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.Context;
62ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport android.content.Intent;
63ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.database.Cursor;
6496293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blankimport android.net.ConnectivityManager;
65d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blankimport android.os.RemoteException;
6677424af660458104b732bdcb718874b17d0cab3aMarc Blankimport android.util.Log;
67ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
6800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.ByteArrayInputStream;
6900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.File;
7000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.FileOutputStream;
7100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException;
7200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream;
7300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.HttpURLConnection;
7400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URI;
7500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URLEncoder;
7600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList;
77ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport java.util.HashMap;
7800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank
79ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankpublic class EasSyncService extends AbstractSyncService {
80ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
811b275b9408d5b856e2482fa3951827489e9585ccMarc Blank    private static final String EMAIL_WINDOW_SIZE = "5";
828047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    public static final String PIM_WINDOW_SIZE = "20";
83ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID =
84ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SERVER_ID + "=?";
859d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private static final String WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING =
869d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL +
87f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank        '=' + Mailbox.CHECK_INTERVAL_PING;
8822bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank    private static final String AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX = " AND " +
89f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank        MailboxColumns.SYNC_INTERVAL + " IN (" + Mailbox.CHECK_INTERVAL_PING +
90f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank        ',' + Mailbox.CHECK_INTERVAL_PUSH + ") AND " + MailboxColumns.TYPE + "!=\"" +
917c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank        Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + '\"';
929d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private static final String WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX =
939d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL +
94f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank        '=' + Mailbox.CHECK_INTERVAL_PUSH_HOLD;
95fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
968047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    static private final int CHUNK_SIZE = 16*1024;
978047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
988047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    static private final String PING_COMMAND = "Ping";
99c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int COMMAND_TIMEOUT = 20*SECONDS;
100c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int PING_COMMAND_TIMEOUT = 20*MINUTES;
101c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
10296293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank    // For mobile, we use a 5 minute timeout (less a few seconds)
10305381a6662f28609e8005023515abb82af00e1d4Marc Blank    static private final int PING_HEARTBEAT_MOBILE = 295;
10496293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank    // For wifi, we use a 15 minute timeout
10505381a6662f28609e8005023515abb82af00e1d4Marc Blank    static private final int PING_HEARTBEAT_WIFI = 900;
10696293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank
107c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    // Fallbacks (in minutes) for ping loop failures
10896293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank    static private final int MAX_PING_FAILURES = 2;
109c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int PING_FALLBACK_INBOX = 5;
1100d88d88926b63042e68c4ea0dc67143ed00d52d0Marc Blank    static private final int PING_FALLBACK_PIM = 30;
111d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
112ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    // Reasonable default
113ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mProtocolVersion = "2.5";
11400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank    public Double mProtocolVersionDouble;
1159d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String mDeviceId = null;
1169d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String mDeviceType = "Android";
1177c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank    AbstractSyncAdapter mTarget;
118ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mAuthString = null;
119ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mCmdString = null;
120ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mHostAddress;
121ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mUserName;
122ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mPassword;
123ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mDomain = null;
124ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    boolean mSentCommands;
125ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    boolean mIsIdle = false;
126ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    boolean mSsl = true;
127ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public Context mContext;
128ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public ContentResolver mContentResolver;
129ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String[] mBindArguments = new String[2];
130ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    InputStream mPendingPartInputStream = null;
131c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    HttpPost mPendingPost = null;
13296293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank    int mNetworkType;
13305381a6662f28609e8005023515abb82af00e1d4Marc Blank    int mPingHeartbeat;
134ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
135ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasSyncService(Context _context, Mailbox _mailbox) {
136ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(_context, _mailbox);
137ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mContext = _context;
138ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mContentResolver = _context.getContentResolver();
139ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HostAuth ha = HostAuth.restoreHostAuthWithId(_context, mAccount.mHostAuthKeyRecv);
140ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0;
141ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
142ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
143ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    private EasSyncService(String prefix) {
144ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(prefix);
145ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
146ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
147ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasSyncService() {
148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        this("EAS Validation");
149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
151ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
152ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void ping() {
153ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        userLog("We've been pinged!");
1541b275b9408d5b856e2482fa3951827489e9585ccMarc Blank        Object synchronizer = getSynchronizer();
1551b275b9408d5b856e2482fa3951827489e9585ccMarc Blank        synchronized (synchronizer) {
1561b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            synchronizer.notify();
157ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
158ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
161ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void stop() {
162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mStop = true;
163c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        synchronized(getSynchronizer()) {
164c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            if (mPendingPost != null) {
165c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                mPendingPost.abort();
166c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            }
167c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
1685c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank    }
169ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
170fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank    @Override
171ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public int getSyncStatus() {
172ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return 0;
173ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
174ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
175368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    private boolean isAuthError(int code) {
17677424af660458104b732bdcb718874b17d0cab3aMarc Blank        return (code == HttpURLConnection.HTTP_UNAUTHORIZED
17777424af660458104b732bdcb718874b17d0cab3aMarc Blank                || code == HttpURLConnection.HTTP_FORBIDDEN
178368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                || code == HttpURLConnection.HTTP_INTERNAL_ERROR);
179368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
180368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
181fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank    @Override
182ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void validateAccount(String hostAddress, String userName, String password, int port,
183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            boolean ssl, Context context) throws MessagingException {
184ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
1857c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            userLog("Testing EAS: " + hostAddress + ", " + userName + ", ssl = " + ssl);
186ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            EasSyncService svc = new EasSyncService("%TestAccount%");
1879d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            svc.mContext = context;
188ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mHostAddress = hostAddress;
189ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mUserName = userName;
190ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mPassword = password;
191ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mSsl = ssl;
1929d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            svc.mDeviceId = SyncManager.getDeviceId();
1938047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            HttpResponse resp = svc.sendHttpClientOptions();
1948047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            int code = resp.getStatusLine().getStatusCode();
1959d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            userLog("Validation (OPTIONS) response: " + code);
196ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (code == HttpURLConnection.HTTP_OK) {
197ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // No exception means successful validation
198ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Validation successful");
199ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return;
200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
201368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            if (isAuthError(code)) {
202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Authentication failed");
203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                throw new AuthenticationFailedException("Validation failed");
204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // TODO Need to catch other kinds of errors (e.g. policy) For now, report the code.
206ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Validation failed, reporting I/O error: " + code);
207ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                throw new MessagingException(MessagingException.IOERROR);
208ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
209ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (IOException e) {
210ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            userLog("IOException caught, reporting I/O error: " + e.getMessage());
211ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            throw new MessagingException(MessagingException.IOERROR);
212ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
213ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
214ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
215ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
21681d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank    private void doStatusCallback(long messageId, long attachmentId, int status) {
217d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        try {
218fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().loadAttachmentStatus(messageId, attachmentId, status, 0);
219fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e) {
220d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            // No danger if the client is no longer around
221d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        }
222d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    }
223ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
22481d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank    private void doProgressCallback(long messageId, long attachmentId, int progress) {
225d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        try {
226fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().loadAttachmentStatus(messageId, attachmentId,
227fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                    EmailServiceStatus.IN_PROGRESS, progress);
228fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e) {
229d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            // No danger if the client is no longer around
230d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        }
231d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    }
232d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
233842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    public File createUniqueFileInternal(String dir, String filename) {
234842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        File directory;
235842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (dir == null) {
236842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory = mContext.getFilesDir();
237842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        } else {
238842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory = new File(dir);
239842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
240842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (!directory.exists()) {
241842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory.mkdirs();
242842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
243842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        File file = new File(directory, filename);
244842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (!file.exists()) {
245842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            return file;
246842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
247842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        // Get the extension of the file, if any.
248842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        int index = filename.lastIndexOf('.');
249842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        String name = filename;
250842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        String extension = "";
251842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (index != -1) {
252842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            name = filename.substring(0, index);
253842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            extension = filename.substring(index);
254842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
255842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        for (int i = 2; i < Integer.MAX_VALUE; i++) {
256842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            file = new File(directory, name + '-' + i + extension);
257842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            if (!file.exists()) {
258842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                return file;
259842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            }
260842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
261842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        return null;
262842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    }
263842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank
264d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    /**
265d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * Loads an attachment, based on the PartRequest passed in.  The PartRequest is basically our
266d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * wrapper for Attachment
267d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * @param req the part (attachment) to be retrieved
268d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * @throws IOException
269d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     */
270842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    protected void getAttachment(PartRequest req) throws IOException {
271d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        Attachment att = req.att;
272b0ce70f8d18dfe14fdd72be528d89eda1ba229feMarc Blank        Message msg = Message.restoreMessageWithId(mContext, att.mMessageKey);
27381d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank        doProgressCallback(msg.mId, att.mId, 0);
274ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        DefaultHttpClient client = new DefaultHttpClient();
275d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        String us = makeUriString("GetAttachment", "&AttachmentName=" + att.mLocation);
276ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HttpPost method = new HttpPost(URI.create(us));
277ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        method.setHeader("Authorization", mAuthString);
278ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
279ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HttpResponse res = client.execute(method);
280ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        int status = res.getStatusLine().getStatusCode();
281ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (status == HttpURLConnection.HTTP_OK) {
282ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            HttpEntity e = res.getEntity();
283ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int len = (int)e.getContentLength();
284ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String type = e.getContentType().getValue();
285ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            InputStream is = res.getEntity().getContent();
286bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler            File f = (req.destination != null)
287bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    ? new File(req.destination)
288bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    : createUniqueFileInternal(req.destination, att.mFileName);
289ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (f != null) {
290bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                // Ensure that the target directory exists
291bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                File destDir = f.getParentFile();
292bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                if (!destDir.exists()) {
293bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    destDir.mkdirs();
294bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                }
295ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                FileOutputStream os = new FileOutputStream(f);
296ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (len > 0) {
297ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    try {
298ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartRequest = req;
299ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartInputStream = is;
300ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        byte[] bytes = new byte[CHUNK_SIZE];
301ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        int length = len;
302ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        while (len > 0) {
303ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int n = (len > CHUNK_SIZE ? CHUNK_SIZE : len);
304ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int read = is.read(bytes, 0, n);
305ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            os.write(bytes, 0, read);
306ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            len -= read;
307d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                            int pct = ((length - len) * 100 / length);
30881d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank                            doProgressCallback(msg.mId, att.mId, pct);
309ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
310ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } finally {
311ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartRequest = null;
312ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartInputStream = null;
313ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
314ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
315ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                os.flush();
316ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                os.close();
317ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
318d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                // EmailProvider will throw an exception if we try to update an unsaved attachment
319d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                if (att.isSaved()) {
320bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    String contentUriString = (req.contentUriString != null)
321bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                            ? req.contentUriString
322bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                            : "file://" + f.getAbsolutePath();
323d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    ContentValues cv = new ContentValues();
324bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    cv.put(AttachmentColumns.CONTENT_URI, contentUriString);
325d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    cv.put(AttachmentColumns.MIME_TYPE, type);
326d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    att.update(mContext, cv);
32781d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank                    doStatusCallback(msg.mId, att.mId, EmailServiceStatus.SUCCESS);
328d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
330d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        } else {
33181d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank            doStatusCallback(msg.mId, att.mId, EmailServiceStatus.MESSAGE_NOT_FOUND);
332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
335147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank    @SuppressWarnings("deprecation")
3369d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String makeUriString(String cmd, String extra) throws IOException {
337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank         // Cache the authentication string and the command string
338ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String safeUserName = URLEncoder.encode(mUserName);
339ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (mAuthString == null) {
340ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String cs = mUserName + ':' + mPassword;
341ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mAuthString = "Basic " + Base64.encodeBytes(cs.getBytes());
342ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId + "&DeviceType="
343ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    + mDeviceType;
344ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
345ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String us = (mSsl ? "https" : "http") + "://" + mHostAddress +
346ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            "/Microsoft-Server-ActiveSync";
347ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (cmd != null) {
348ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            us += "?Cmd=" + cmd + mCmdString;
349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
350ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (extra != null) {
351ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            us += extra;
352ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
353ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return us;
354ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
355ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3568047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private void setHeaders(HttpRequestBase method) {
3578047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("Authorization", mAuthString);
3588047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("MS-ASProtocolVersion", mProtocolVersion);
3598047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("Connection", "keep-alive");
3608047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("User-Agent", mDeviceType + '/' + Eas.VERSION);
3618047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
362ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3638047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private HttpClient getHttpClient(int timeout) {
3648047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpParams params = new BasicHttpParams();
365c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        HttpConnectionParams.setConnectionTimeout(params, 10*SECONDS);
3668047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpConnectionParams.setSoTimeout(params, timeout);
3678047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        return new DefaultHttpClient(params);
3688047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
369ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3708047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    protected HttpResponse sendHttpClientPost(String cmd, byte[] bytes) throws IOException {
3718047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpClient client =
3728047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            getHttpClient(cmd.equals(PING_COMMAND) ? PING_COMMAND_TIMEOUT : COMMAND_TIMEOUT);
3738047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        String us = makeUriString(cmd, null);
3748047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpPost method = new HttpPost(URI.create(us));
3758047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        if (cmd.startsWith("SendMail&")) {
3768047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            method.setHeader("Content-Type", "message/rfc822");
3778047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        } else {
3788047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml");
379ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
3808047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        setHeaders(method);
3818047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setEntity(new ByteArrayEntity(bytes));
382c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        synchronized(getSynchronizer()) {
383c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            mPendingPost = method;
384c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
385c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        try {
386c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            return client.execute(method);
387c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        } finally {
388c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            synchronized(getSynchronizer()) {
389c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                mPendingPost = null;
390c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            }
391c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
3928047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
3938047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
3948047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    protected HttpResponse sendHttpClientOptions() throws IOException {
3958047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpClient client = getHttpClient(COMMAND_TIMEOUT);
3968047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        String us = makeUriString("OPTIONS", null);
3978047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpOptions method = new HttpOptions(URI.create(us));
3988047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        setHeaders(method);
3998047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        return client.execute(method);
400ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
401ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
402ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String getTargetCollectionClassFromCursor(Cursor c) {
403ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
404ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (type == Mailbox.TYPE_CONTACTS) {
405ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Contacts";
406ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else if (type == Mailbox.TYPE_CALENDAR) {
407ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Calendar";
408ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else {
409ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Email";
410ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
411ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
412ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
413ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /**
414ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * Performs FolderSync
415ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     *
416ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @throws IOException
417ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @throws EasParserException
418ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
419a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank    public void runAccountMailbox() throws IOException, EasParserException {
420fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        // Initialize exit status to success
42196293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank        mNetworkType = waitForConnectivity();
42205381a6662f28609e8005023515abb82af00e1d4Marc Blank        mPingHeartbeat = PING_HEARTBEAT_MOBILE;
42305381a6662f28609e8005023515abb82af00e1d4Marc Blank        if (mNetworkType == ConnectivityManager.TYPE_WIFI) {
42405381a6662f28609e8005023515abb82af00e1d4Marc Blank            mPingHeartbeat = PING_HEARTBEAT_WIFI;
42505381a6662f28609e8005023515abb82af00e1d4Marc Blank        }
426fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        mExitStatus = EmailServiceStatus.SUCCESS;
427ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
428fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            try {
429fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                SyncManager.callback()
430fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                    .syncMailboxListStatus(mAccount.mId, EmailServiceStatus.IN_PROGRESS, 0);
431fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            } catch (RemoteException e1) {
432fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                // Don't care if this fails
433fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
434fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
435ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mAccount.mSyncKey == null) {
436ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mAccount.mSyncKey = "0";
4371b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Account syncKey INIT to 0");
4389387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                ContentValues cv = new ContentValues();
4399387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
4409387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                mAccount.update(mContext, cv);
441ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
442ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
4439d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            boolean firstSync = mAccount.mSyncKey.equals("0");
4449d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            if (firstSync) {
4451b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Initial FolderSync");
4461b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            }
4471b275b9408d5b856e2482fa3951827489e9585ccMarc Blank
448ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // When we first start up, change all ping mailboxes to push.
449ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ContentValues cv = new ContentValues();
450f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank            cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
451ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
4529d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING,
4539d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    new String[] {Long.toString(mAccount.mId)}) > 0) {
4549d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                SyncManager.kick("change ping boxes to push");
455ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
456ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
4570f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            // Determine our protocol version, if we haven't already
4580f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            if (mAccount.mProtocolVersion == null) {
4591b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Determine EAS protocol version");
4608047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                HttpResponse resp = sendHttpClientOptions();
4618047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                int code = resp.getStatusLine().getStatusCode();
4628047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                userLog("OPTIONS response: " + code);
4638047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                if (code == HttpURLConnection.HTTP_OK) {
4648047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    Header header = resp.getFirstHeader("ms-asprotocolversions");
4658047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    String versions = header.getValue();
4668047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    if (versions != null) {
4678047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        if (versions.contains("12.0")) {
4688047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                            mProtocolVersion = "12.0";
469ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
4708047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
4718047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        mAccount.mProtocolVersion = mProtocolVersion;
4728047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        userLog(versions);
4738047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        userLog("Using version " + mProtocolVersion);
474ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } else {
4758047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        errorLog("No protocol versions in OPTIONS response");
476ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        throw new IOException();
477ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
4788047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                } else {
4798047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    errorLog("OPTIONS command failed; throwing IOException");
4808047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    throw new IOException();
4810f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
4820f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            }
4831b275b9408d5b856e2482fa3951827489e9585ccMarc Blank
48477424af660458104b732bdcb718874b17d0cab3aMarc Blank            // Change all pushable boxes to push when we start the account mailbox
48577424af660458104b732bdcb718874b17d0cab3aMarc Blank            if (mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH) {
48677424af660458104b732bdcb718874b17d0cab3aMarc Blank                cv = new ContentValues();
487f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank                cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH);
48877424af660458104b732bdcb718874b17d0cab3aMarc Blank                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
48977424af660458104b732bdcb718874b17d0cab3aMarc Blank                        SyncManager.WHERE_IN_ACCOUNT_AND_PUSHABLE,
49077424af660458104b732bdcb718874b17d0cab3aMarc Blank                        new String[] {Long.toString(mAccount.mId)}) > 0) {
49177424af660458104b732bdcb718874b17d0cab3aMarc Blank                    userLog("Push account; set pushable boxes to push...");
49277424af660458104b732bdcb718874b17d0cab3aMarc Blank                }
49377424af660458104b732bdcb718874b17d0cab3aMarc Blank            }
49477424af660458104b732bdcb718874b17d0cab3aMarc Blank
49577424af660458104b732bdcb718874b17d0cab3aMarc Blank            while (!mStop) {
4968047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 userLog("Sending Account syncKey: " + mAccount.mSyncKey);
4978047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 Serializer s = new Serializer();
4988047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
49977424af660458104b732bdcb718874b17d0cab3aMarc Blank                     .text(mAccount.mSyncKey).end().end().done();
5008047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 HttpResponse resp = sendHttpClientPost("FolderSync", s.toByteArray());
5018047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 if (mStop) break;
5028047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 int code = resp.getStatusLine().getStatusCode();
5038047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 if (code == HttpURLConnection.HTTP_OK) {
5048047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     HttpEntity entity = resp.getEntity();
5058047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     int len = (int)entity.getContentLength();
5068047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     if (len > 0) {
5078047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         InputStream is = entity.getContent();
5088047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         // Returns true if we need to sync again
509f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank                         userLog("FolderSync, deviceId = " + mDeviceId);
5108047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         if (new FolderSyncParser(is, this).parse()) {
5118047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                             continue;
5128047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         }
5138047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     }
5148047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 } else if (code == HttpURLConnection.HTTP_UNAUTHORIZED ||
5150f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                        code == HttpURLConnection.HTTP_FORBIDDEN) {
5160f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    mExitStatus = AbstractSyncService.EXIT_LOGIN_FAILURE;
5170f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } else {
5180f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    userLog("FolderSync response error: " + code);
5190f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
520ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
5219d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                // Change all push/hold boxes to push
5229d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                cv = new ContentValues();
5239d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
5249d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
5259d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX,
5269d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        new String[] {Long.toString(mAccount.mId)}) > 0) {
5279d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    userLog("Set push/hold boxes to push...");
5289d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                }
5299d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
5300f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                try {
5310f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    SyncManager.callback()
5329d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        .syncMailboxListStatus(mAccount.mId, mExitStatus, 0);
5330f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } catch (RemoteException e1) {
5340f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    // Don't care if this fails
5350f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
536fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
5370f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                // Wait for push notifications.
5380f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                String threadName = Thread.currentThread().getName();
5390f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                try {
5400f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    runPingLoop();
5410f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } catch (StaleFolderListException e) {
5420f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    // We break out if we get told about a stale folder list
5430f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    userLog("Ping interrupted; folder list requires sync...");
5440f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } finally {
5450f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    Thread.currentThread().setName(threadName);
5460f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
547ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
5480f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank         } catch (IOException e) {
549fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // We catch this here to send the folder sync status callback
550fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // A folder sync failed callback will get sent from run()
551fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            try {
5524d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                if (!mStop) {
5534d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    SyncManager.callback()
5544d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                        .syncMailboxListStatus(mAccount.mId,
5554d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                                EmailServiceStatus.CONNECTION_ERROR, 0);
5564d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                }
557fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            } catch (RemoteException e1) {
558fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                // Don't care if this fails
559fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
56096293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank            throw e;
561ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
562ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
563ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
564c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    void pushFallback(long mailboxId) {
565c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
566c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        ContentValues cv = new ContentValues();
567c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        if (mailbox.mType == Mailbox.TYPE_INBOX) {
568c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            cv.put(Mailbox.SYNC_INTERVAL, PING_FALLBACK_INBOX);
569c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            errorLog("*** PING LOOP: Turning off push due to ping loop on inbox...");
570ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            mContentResolver.update(Mailbox.CONTENT_URI, cv,
571ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    MailboxColumns.ACCOUNT_KEY + '=' + mAccount.mId
572ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    + AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX, null);
573ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            // Now, change the account as well
574ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            cv.clear();
575c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            cv.put(Account.SYNC_INTERVAL, PING_FALLBACK_INBOX);
576ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            mContentResolver.update(ContentUris.withAppendedId(Account.CONTENT_URI, mAccount.mId),
577ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    cv, null, null);
578c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
57977424af660458104b732bdcb718874b17d0cab3aMarc Blank            // Let the SyncManager know that something's changed
58077424af660458104b732bdcb718874b17d0cab3aMarc Blank            SyncManager.kick("push fallback");
58177424af660458104b732bdcb718874b17d0cab3aMarc Blank
582ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            // TODO Discuss the best way to alert the user
583ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            // Alert the user about what we've done
584ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            NotificationManager nm = (NotificationManager)mContext
585ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                .getSystemService(Context.NOTIFICATION_SERVICE);
586ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            Notification note =
587ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                new Notification(R.drawable.stat_notify_email_generic,
588ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        mContext.getString(R.string.notification_ping_loop_title),
589ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        System.currentTimeMillis());
590ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            Intent i = new Intent(mContext, AccountFolderList.class);
591ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            PendingIntent pi = PendingIntent.getActivity(mContext, 0, i, 0);
592ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            note.setLatestEventInfo(mContext,
593ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    mContext.getString(R.string.notification_ping_loop_title),
594ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    mContext.getString(R.string.notification_ping_loop_text), pi);
595ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            nm.notify(Eas.EXCHANGE_ERROR_NOTIFICATION, note);
596c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        } else {
597c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            // Just change this box to sync every 10 minutes
598c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            cv.put(Mailbox.SYNC_INTERVAL, PING_FALLBACK_PIM);
599c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
600c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                    cv, null, null);
601c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            errorLog("*** PING LOOP: Backing off sync of " + mailbox.mDisplayName + " to 10 mins");
602c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
603368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
604368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
605ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    void runPingLoop() throws IOException, StaleFolderListException {
606ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // Do push for all sync services here
607368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        ArrayList<Mailbox> pushBoxes = new ArrayList<Mailbox>();
608c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        long endTime = System.currentTimeMillis() + (30*MINUTES);
609ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank        HashMap<Long, Integer> pingFailureMap = new HashMap<Long, Integer>();
610ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
611ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        while (System.currentTimeMillis() < endTime) {
612ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Count of pushable mailboxes
613ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int pushCount = 0;
614ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Count of mailboxes that can be pushed right now
615ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int canPushCount = 0;
6167c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            Serializer s = new Serializer();
617ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int code;
618ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
61922bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                    MailboxColumns.ACCOUNT_KEY + '=' + mAccount.mId +
62022bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                    AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX, null, null);
621ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
622368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            pushBoxes.clear();
623368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
624ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            try {
625ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Loop through our pushed boxes seeing what is available to push
626ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                while (c.moveToNext()) {
627ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    pushCount++;
628ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // Two requirements for push:
629ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // 1) SyncManager tells us the mailbox is syncable (not running, not stopped)
630ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // 2) The syncKey isn't "0" (i.e. it's synced at least once)
631ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
63277424af660458104b732bdcb718874b17d0cab3aMarc Blank                    int pingStatus = SyncManager.pingStatus(mailboxId);
63377424af660458104b732bdcb718874b17d0cab3aMarc Blank                    String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
63477424af660458104b732bdcb718874b17d0cab3aMarc Blank                    if (pingStatus == SyncManager.PING_STATUS_OK) {
635ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN);
636ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        if (syncKey == null || syncKey.equals("0")) {
63777424af660458104b732bdcb718874b17d0cab3aMarc Blank                            pushCount--;
638ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            continue;
639ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
640ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank
641ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // Take a peek at this box's behavior last sync
642ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // We do this because some Exchange 2003 servers put themselves (and
643ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // therefore our client) into a "ping loop" in which the client is
644ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // continuously told of server changes, only to find that there aren't any.
645ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // This behavior is seemingly random, and we must code defensively by
646ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // backing off of push behavior when this is detected.
647ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // The server fix is at http://support.microsoft.com/kb/923282
648ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank
649ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // Sync status is encoded as S<type>:<exitstatus>:<changes>
650ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        String status = c.getString(Mailbox.CONTENT_SYNC_STATUS_COLUMN);
651ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        int type = SyncManager.getStatusType(status);
652ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        if (type == SyncManager.SYNC_PING) {
653ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                            int changeCount = SyncManager.getStatusChangeCount(status);
654c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                            if (changeCount > 0) {
655c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                                pingFailureMap.remove(mailboxId);
656c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                            } else if (changeCount == 0) {
657ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                // This means that a ping failed; we'll keep track of this
658ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                Integer failures = pingFailureMap.get(mailboxId);
659ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                if (failures == null) {
660ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                    pingFailureMap.put(mailboxId, 1);
66196293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank                                } else if (failures > MAX_PING_FAILURES) {
662ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                    // Change all push/ping boxes (except account) to 5 minute sync
663c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                                    pushFallback(mailboxId);
664ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                    return;
665ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                } else {
666ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                    pingFailureMap.put(mailboxId, failures + 1);
667ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                }
668ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                            }
669ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        }
670ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank
671ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        if (canPushCount++ == 0) {
672ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            // Initialize the Ping command
67396293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank                            s.start(Tags.PING_PING)
67405381a6662f28609e8005023515abb82af00e1d4Marc Blank                                .data(Tags.PING_HEARTBEAT_INTERVAL,
67505381a6662f28609e8005023515abb82af00e1d4Marc Blank                                        Integer.toString(mPingHeartbeat))
6767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                                .start(Tags.PING_FOLDERS);
677ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
678ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // When we're ready for Calendar/Contacts, we will check folder type
679ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // TODO Save Calendar and Contacts!! Mark as not visible!
680ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String folderClass = getTargetCollectionClassFromCursor(c);
6817c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                        s.start(Tags.PING_FOLDER)
6827c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .data(Tags.PING_ID, c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN))
6837c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .data(Tags.PING_CLASS, folderClass)
6847c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .end();
68577424af660458104b732bdcb718874b17d0cab3aMarc Blank                        userLog("Ping ready for: " + folderClass + ", " + mailboxName + " (" +
68677424af660458104b732bdcb718874b17d0cab3aMarc Blank                                c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN) + ')');
687368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                        pushBoxes.add(new Mailbox().restore(c));
68877424af660458104b732bdcb718874b17d0cab3aMarc Blank                    } else if (pingStatus == SyncManager.PING_STATUS_RUNNING ||
68977424af660458104b732bdcb718874b17d0cab3aMarc Blank                            pingStatus == SyncManager.PING_STATUS_WAITING) {
69077424af660458104b732bdcb718874b17d0cab3aMarc Blank                        userLog(mailboxName + " not ready for ping");
69177424af660458104b732bdcb718874b17d0cab3aMarc Blank                    } else if (pingStatus == SyncManager.PING_STATUS_UNABLE) {
69277424af660458104b732bdcb718874b17d0cab3aMarc Blank                        pushCount--;
69377424af660458104b732bdcb718874b17d0cab3aMarc Blank                        userLog(mailboxName + " in error state; ignore");
69477424af660458104b732bdcb718874b17d0cab3aMarc Blank                        continue;
695ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
696ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
697ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } finally {
698ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                c.close();
699ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
700ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
70122bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank            if (canPushCount > 0 && (canPushCount == pushCount)) {
702ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // If we have some number that are ready for push, send Ping to the server
7037c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.end().end().done();
7047c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank
705d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                Thread.currentThread().setName(mAccount.mDisplayName + ": Ping");
70605381a6662f28609e8005023515abb82af00e1d4Marc Blank                userLog("Sending ping, timeout: " + mPingHeartbeat + "s");
707f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank
70805381a6662f28609e8005023515abb82af00e1d4Marc Blank                // Sleep for the heartbeat time plus a little bit of slack
70905381a6662f28609e8005023515abb82af00e1d4Marc Blank                SyncManager.runAsleep(mMailboxId, (mPingHeartbeat+15)*SECONDS);
710c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                long time = System.currentTimeMillis();
711f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank                HttpResponse res = sendHttpClientPost(PING_COMMAND, s.toByteArray());
712f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank                SyncManager.runAwake(mMailboxId);
7138047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
7144d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // Don't send request if we've been asked to stop
7154d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                if (mStop) return;
7168047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                code = res.getStatusLine().getStatusCode();
717368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
718c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                // Get elapsed time
719c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                time = System.currentTimeMillis() - time;
720c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                userLog("Ping response: " + code + " in " + time + "ms");
721c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
7224d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // Return immediately if we've been asked to stop
723842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                if (mStop) {
724368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                    userLog("Stopping pingLoop");
725842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                    return;
726842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                }
727368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
728ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (code == HttpURLConnection.HTTP_OK) {
7298047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    HttpEntity e = res.getEntity();
7308047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    int len = (int)e.getContentLength();
7318047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    InputStream is = res.getEntity().getContent();
7328047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    if (len > 0) {
7338047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        parsePingResult(is, mContentResolver);
734ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } else {
735ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        throw new IOException();
736ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
737368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                } else if (isAuthError(code)) {
738ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    mExitStatus = AbstractSyncService.EXIT_LOGIN_FAILURE;
739ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    userLog("Authorization error during Ping: " + code);
740ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    throw new IOException();
741ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
742ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else if (pushCount > 0) {
743ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // If we want to Ping, but can't just yet, wait 10 seconds and try again
74422bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                userLog("pingLoop waiting for " + (pushCount - canPushCount) + " box(es)");
745c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                sleep(10*SECONDS);
746ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
747c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                // We've got nothing to do, so we'll check again in 30 minutes at which time
748c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                // we'll update the folder list.
749c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                SyncManager.runAsleep(mMailboxId, 30*MINUTES);
750c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                sleep(30*MINUTES);
75177424af660458104b732bdcb718874b17d0cab3aMarc Blank                SyncManager.runAwake(mMailboxId);
752ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
753ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
754ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
755ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
756ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    void sleep(long ms) {
757ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
758ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Thread.sleep(ms);
759ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (InterruptedException e) {
760ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Doesn't matter whether we stop early; it's the thought that counts
761ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
762ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
763ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
7648047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private int parsePingResult(InputStream is, ContentResolver cr)
765ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        throws IOException, StaleFolderListException {
7668047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        PingParser pp = new PingParser(is, this);
767ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (pp.parse()) {
768ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // True indicates some mailboxes need syncing...
769ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // syncList has the serverId's of the mailboxes...
770ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mBindArguments[0] = Long.toString(mAccount.mId);
771ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<String> syncList = pp.getSyncList();
772ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            for (String serverId: syncList) {
773d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                mBindArguments[1] = serverId;
774ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                Cursor c = cr.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
775ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        WHERE_ACCOUNT_KEY_AND_SERVER_ID, mBindArguments, null);
776ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                try {
777ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (c.moveToFirst()) {
7784d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                        SyncManager.startManualSync(c.getLong(Mailbox.CONTENT_ID_COLUMN),
779ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                SyncManager.SYNC_PING, null);
780ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
781ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } finally {
782ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    c.close();
783ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
784ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
785ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
7867c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank        return pp.getSyncList().size();
787ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
788ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
789ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    ByteArrayInputStream readResponse(HttpURLConnection uc) throws IOException {
790ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String encoding = uc.getHeaderField("Transfer-Encoding");
791ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (encoding == null) {
792ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int len = uc.getHeaderFieldInt("Content-Length", 0);
793ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (len > 0) {
794ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                InputStream in = uc.getInputStream();
795ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                byte[] bytes = new byte[len];
796ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                int remain = len;
797ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                int offs = 0;
798ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                while (remain > 0) {
799ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    int read = in.read(bytes, offs, remain);
800ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    remain -= read;
801ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    offs += read;
802ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
803ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return new ByteArrayInputStream(bytes);
804ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
805ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else if (encoding.equalsIgnoreCase("chunked")) {
806ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // TODO We don't handle this yet
807ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return null;
808ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
809ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return null;
810ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
811ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
812ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String readResponseString(HttpURLConnection uc) throws IOException {
813ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String encoding = uc.getHeaderField("Transfer-Encoding");
814ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (encoding == null) {
815ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int len = uc.getHeaderFieldInt("Content-Length", 0);
816ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (len > 0) {
817ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                InputStream in = uc.getInputStream();
818ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                byte[] bytes = new byte[len];
819ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                int remain = len;
820ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                int offs = 0;
821ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                while (remain > 0) {
822ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    int read = in.read(bytes, offs, remain);
823ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    remain -= read;
824ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    offs += read;
825ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
826ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return new String(bytes);
827ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
828ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else if (encoding.equalsIgnoreCase("chunked")) {
829ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // TODO We don't handle this yet
830ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return null;
831ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
832ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return null;
833ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
834ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
835368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    private String getFilterType() {
836368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        String filter = Eas.FILTER_1_WEEK;
837368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        switch (mAccount.mSyncLookback) {
838368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_DAY: {
839368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_DAY;
840368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
841368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
842368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_3_DAYS: {
843368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_3_DAYS;
844368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
845368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
846368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_WEEK: {
847368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_WEEK;
848368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
849368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
850368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_2_WEEKS: {
851368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_2_WEEKS;
852368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
853368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
854368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_MONTH: {
855368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_MONTH;
856368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
857368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
858368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_ALL: {
859368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_ALL;
860368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
861368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
862368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        }
863368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        return filter;
864368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
865368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
866ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /**
867ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * Common code to sync E+PIM data
868ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     *
869ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @param target, an EasMailbox, EasContacts, or EasCalendar object
870ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
8717c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank    public void sync(AbstractSyncAdapter target) throws IOException {
872ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mTarget = target;
873ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        Mailbox mailbox = target.mMailbox;
874ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
875ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        boolean moreAvailable = true;
876ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        while (!mStop && moreAvailable) {
877ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            waitForConnectivity();
878ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
879d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            while (true) {
880d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                PartRequest req = null;
881d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                synchronized (mPartRequests) {
882d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    if (mPartRequests.isEmpty()) {
883d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        break;
884d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    } else {
885d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        req = mPartRequests.get(0);
886d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    }
887d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
888842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                getAttachment(req);
889d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                synchronized(mPartRequests) {
890d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    mPartRequests.remove(req);
891d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
892d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            }
893d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
8947c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            Serializer s = new Serializer();
895ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mailbox.mSyncKey == null) {
896ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Mailbox syncKey RESET");
897ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mailbox.mSyncKey = "0";
898ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
899ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String className = target.getCollectionName();
900ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            userLog("Sending " + className + " syncKey: " + mailbox.mSyncKey);
9017c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.start(Tags.SYNC_SYNC)
9027c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .start(Tags.SYNC_COLLECTIONS)
9037c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .start(Tags.SYNC_COLLECTION)
9047c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_CLASS, className)
9057c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_SYNC_KEY, mailbox.mSyncKey)
9067c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId)
9077c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .tag(Tags.SYNC_DELETES_AS_MOVES);
908ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
909ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // EAS doesn't like GetChanges if the syncKey is "0"; not documented
910ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (!mailbox.mSyncKey.equals("0")) {
9117c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.tag(Tags.SYNC_GET_CHANGES);
912ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
9131b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            s.data(Tags.SYNC_WINDOW_SIZE,
9141b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                    className.equals("Email") ? EMAIL_WINDOW_SIZE : PIM_WINDOW_SIZE);
915ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            boolean options = false;
916ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (!className.equals("Contacts")) {
917ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Set the lookback appropriately (EAS calls this a "filter")
918368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                s.start(Tags.SYNC_OPTIONS).data(Tags.SYNC_FILTER_TYPE, getFilterType());
9197c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                // No truncation in this version
9207c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                //if (mProtocolVersionDouble < 12.0) {
9217c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                //    s.data(Tags.SYNC_TRUNCATION, "7");
9227c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                //}
92300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                options = true;
924ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
92500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            if (mProtocolVersionDouble >= 12.0) {
926ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (!options) {
927ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    options = true;
9287c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    s.start(Tags.SYNC_OPTIONS);
929ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
9307c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.start(Tags.BASE_BODY_PREFERENCE)
931368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                    // HTML for email; plain text for everything else
932c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                    .data(Tags.BASE_TYPE, (className.equals("Email") ? Eas.BODY_PREFERENCE_HTML
933368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                            : Eas.BODY_PREFERENCE_TEXT))
934368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                // No truncation in this version
935368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                //.data(Tags.BASE_TRUNCATION_SIZE, Eas.DEFAULT_BODY_TRUNCATION_SIZE)
9367c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    .end();
937ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
938ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (options) {
9397c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.end();
940ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
941ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
942ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Send our changes up to the server
943ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            target.sendLocalChanges(s, this);
944ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
9457c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.end().end().end().done();
946c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            userLog("Sync, deviceId = " + mDeviceId);
9478047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            HttpResponse resp = sendHttpClientPost("Sync", s.toByteArray());
9488047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            int code = resp.getStatusLine().getStatusCode();
949ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (code == HttpURLConnection.HTTP_OK) {
9508047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 InputStream is = resp.getEntity().getContent();
951ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (is != null) {
952ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    moreAvailable = target.parse(is, this);
9538480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank                    target.cleanup(this);
954ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
955ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
956ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Sync response error: " + code);
957368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                if (isAuthError(code)) {
958ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    mExitStatus = AbstractSyncService.EXIT_LOGIN_FAILURE;
959ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
960ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return;
961ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
962ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
963ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
964ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
965ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /* (non-Javadoc)
966ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @see java.lang.Runnable#run()
967ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
968ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void run() {
969ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mThread = Thread.currentThread();
970ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        TAG = mThread.getName();
9719d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
972ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HostAuth ha = HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
973ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mHostAddress = ha.mAddress;
974ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mUserName = ha.mLogin;
975ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mPassword = ha.mPassword;
976ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
977fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        try {
978fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().syncMailboxStatus(mMailboxId, EmailServiceStatus.IN_PROGRESS, 0);
979fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e1) {
980fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // Don't care if this fails
981fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        }
982fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
983ab701c862d6e380c8e09ac53ed493aec5d523d3dMarc Blank        // Make sure account and mailbox are always the latest from the database
984ab701c862d6e380c8e09ac53ed493aec5d523d3dMarc Blank        mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
985ab701c862d6e380c8e09ac53ed493aec5d523d3dMarc Blank        mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId);
986a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank        // Whether or not we're the account mailbox
987ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
9889d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            mDeviceId = SyncManager.getDeviceId();
989147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank            if (mMailbox == null || mAccount == null) {
990147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank                return;
9917c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            } else if (mMailbox.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
992a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank                runAccountMailbox();
993ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
9947c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                AbstractSyncAdapter target;
99500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
99600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mProtocolVersion = mAccount.mProtocolVersion;
99700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
998ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (mMailbox.mType == Mailbox.TYPE_CONTACTS)
9997c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    target = new ContactsSyncAdapter(mMailbox, this);
1000ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                else {
10017c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    target = new EmailSyncAdapter(mMailbox, this);
1002ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
1003ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // We loop here because someone might have put a request in while we were syncing
1004ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // and we've missed that opportunity...
1005ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                do {
1006ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (mRequestTime != 0) {
1007ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        userLog("Looping for user request...");
1008ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mRequestTime = 0;
1009ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
1010ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    sync(target);
1011ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } while (mRequestTime != 0);
1012ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
1013ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mExitStatus = EXIT_DONE;
1014ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (IOException e) {
1015f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank            String message = e.getMessage();
1016f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank            userLog("Caught IOException: " + ((message == null) ? "" : message));
1017ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mExitStatus = EXIT_IO_ERROR;
1018ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (Exception e) {
101977424af660458104b732bdcb718874b17d0cab3aMarc Blank            Log.e(TAG, "Uncaught exception in EasSyncService", e);
1020ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } finally {
10214d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank            if (!mStop) {
10224d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                userLog(mMailbox.mDisplayName + ": sync finished");
10234d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                SyncManager.done(this);
10244d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // If this is the account mailbox, wake up SyncManager
10254d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // Because this box has a "push" interval, it will be restarted immediately
10264d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // which will cause the folder list to be reloaded...
10275c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                int status;
10285c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                switch (mExitStatus) {
10295c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_IO_ERROR:
10305c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.CONNECTION_ERROR;
10315c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
10325c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_DONE:
10335c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.SUCCESS;
10345c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
10355c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_LOGIN_FAILURE:
10365c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.LOGIN_FAILED;
10375c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
10385c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    default:
10395c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.REMOTE_EXCEPTION;
104077424af660458104b732bdcb718874b17d0cab3aMarc Blank                        errorLog("Sync ended due to an exception.");
10415c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
10425c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                }
1043c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
10444d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                try {
10454d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    SyncManager.callback().syncMailboxStatus(mMailboxId, status, 0);
10464d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                } catch (RemoteException e1) {
10474d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    // Don't care if this fails
10484d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                }
10495c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank
10505c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                // Save the sync time and status
10515c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                ContentValues cv = new ContentValues();
10525c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
10535c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                String s = "S" + mSyncReason + ':' + status + ':' + mChangeCount;
10545c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                cv.put(Mailbox.SYNC_STATUS, s);
10555c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                mContentResolver.update(ContentUris
10565c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        .withAppendedId(Mailbox.CONTENT_URI, mMailboxId), cv, null, null);
10574d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank            } else {
10584d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                userLog(mMailbox.mDisplayName + ": stopped thread finished.");
1059fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
10609d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
1061c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            // Make sure SyncManager knows about this
1062c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            SyncManager.kick("sync finished");
10639d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank       }
1064ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
1065ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank}
1066