EasSyncService.java revision c1e79c036cd2a40e8a6e66b8ea4d37d121d355ba
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;
64d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blankimport android.os.RemoteException;
6577424af660458104b732bdcb718874b17d0cab3aMarc Blankimport android.util.Log;
66ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
6700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.ByteArrayInputStream;
6800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.File;
6900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.FileOutputStream;
7000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException;
7100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream;
7200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.HttpURLConnection;
7300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URI;
7400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URLEncoder;
7500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList;
76ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport java.util.HashMap;
7700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank
78ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankpublic class EasSyncService extends AbstractSyncService {
79ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
801b275b9408d5b856e2482fa3951827489e9585ccMarc Blank    private static final String EMAIL_WINDOW_SIZE = "5";
818047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    public static final String PIM_WINDOW_SIZE = "20";
82ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID =
83ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SERVER_ID + "=?";
849d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private static final String WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING =
859d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL +
869d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank        '=' + Account.CHECK_INTERVAL_PING;
8722bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank    private static final String AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX = " AND " +
889387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler        MailboxColumns.SYNC_INTERVAL + " IN (" + Account.CHECK_INTERVAL_PING +
897c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank        ',' + Account.CHECK_INTERVAL_PUSH + ") AND " + MailboxColumns.TYPE + "!=\"" +
907c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank        Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + '\"';
919d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private static final String WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX =
929d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank        MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL +
939d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank        '=' + Account.CHECK_INTERVAL_PUSH_HOLD;
94fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
958047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    static private final int CHUNK_SIZE = 16*1024;
968047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
978047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    static private final String PING_COMMAND = "Ping";
98c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int COMMAND_TIMEOUT = 20*SECONDS;
99c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int PING_COMMAND_TIMEOUT = 20*MINUTES;
100c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
101c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    // Fallbacks (in minutes) for ping loop failures
102c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int PING_FALLBACK_INBOX = 5;
103c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    static private final int PING_FALLBACK_PIM = 10;
104d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
105ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    // Reasonable default
106ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mProtocolVersion = "2.5";
10700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank    public Double mProtocolVersionDouble;
1089d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String mDeviceId = null;
1099d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String mDeviceType = "Android";
1107c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank    AbstractSyncAdapter mTarget;
111ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mAuthString = null;
112ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mCmdString = null;
113ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mHostAddress;
114ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mUserName;
115ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String mPassword;
116ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String mDomain = null;
117ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    boolean mSentCommands;
118ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    boolean mIsIdle = false;
119ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    boolean mSsl = true;
120ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public Context mContext;
121ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public ContentResolver mContentResolver;
122ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String[] mBindArguments = new String[2];
123ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    InputStream mPendingPartInputStream = null;
124c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    HttpPost mPendingPost = null;
125ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
126ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasSyncService(Context _context, Mailbox _mailbox) {
127ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(_context, _mailbox);
128ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mContext = _context;
129ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mContentResolver = _context.getContentResolver();
130ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HostAuth ha = HostAuth.restoreHostAuthWithId(_context, mAccount.mHostAuthKeyRecv);
131ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0;
132ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
133ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
134ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    private EasSyncService(String prefix) {
135ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(prefix);
136ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
137ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
138ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasSyncService() {
139ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        this("EAS Validation");
140ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
141ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
142ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
143ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void ping() {
144ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        userLog("We've been pinged!");
1451b275b9408d5b856e2482fa3951827489e9585ccMarc Blank        Object synchronizer = getSynchronizer();
1461b275b9408d5b856e2482fa3951827489e9585ccMarc Blank        synchronized (synchronizer) {
1471b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            synchronizer.notify();
148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
151ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
152ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void stop() {
153ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mStop = true;
154c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        synchronized(getSynchronizer()) {
155c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            if (mPendingPost != null) {
156c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                mPendingPost.abort();
157c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            }
158c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
1595c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank    }
160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
161fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank    @Override
162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public int getSyncStatus() {
163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return 0;
164ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
165ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
166368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    private boolean isAuthError(int code) {
16777424af660458104b732bdcb718874b17d0cab3aMarc Blank        return (code == HttpURLConnection.HTTP_UNAUTHORIZED
16877424af660458104b732bdcb718874b17d0cab3aMarc Blank                || code == HttpURLConnection.HTTP_FORBIDDEN
169368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                || code == HttpURLConnection.HTTP_INTERNAL_ERROR);
170368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
171368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
172fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank    @Override
173ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void validateAccount(String hostAddress, String userName, String password, int port,
174ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            boolean ssl, Context context) throws MessagingException {
175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
1767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            userLog("Testing EAS: " + hostAddress + ", " + userName + ", ssl = " + ssl);
177ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            EasSyncService svc = new EasSyncService("%TestAccount%");
1789d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            svc.mContext = context;
179ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mHostAddress = hostAddress;
180ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mUserName = userName;
181ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mPassword = password;
182ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            svc.mSsl = ssl;
1839d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            svc.mDeviceId = SyncManager.getDeviceId();
1848047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            HttpResponse resp = svc.sendHttpClientOptions();
1858047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            int code = resp.getStatusLine().getStatusCode();
1869d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            userLog("Validation (OPTIONS) response: " + code);
187ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (code == HttpURLConnection.HTTP_OK) {
188ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // No exception means successful validation
189ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Validation successful");
190ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return;
191ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
192368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            if (isAuthError(code)) {
193ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Authentication failed");
194ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                throw new AuthenticationFailedException("Validation failed");
195ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
196ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // TODO Need to catch other kinds of errors (e.g. policy) For now, report the code.
197ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Validation failed, reporting I/O error: " + code);
198ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                throw new MessagingException(MessagingException.IOERROR);
199ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (IOException e) {
201ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            userLog("IOException caught, reporting I/O error: " + e.getMessage());
202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            throw new MessagingException(MessagingException.IOERROR);
203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
206ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
20781d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank    private void doStatusCallback(long messageId, long attachmentId, int status) {
208d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        try {
209fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().loadAttachmentStatus(messageId, attachmentId, status, 0);
210fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e) {
211d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            // No danger if the client is no longer around
212d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        }
213d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    }
214ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
21581d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank    private void doProgressCallback(long messageId, long attachmentId, int progress) {
216d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        try {
217fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().loadAttachmentStatus(messageId, attachmentId,
218fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                    EmailServiceStatus.IN_PROGRESS, progress);
219fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e) {
220d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            // No danger if the client is no longer around
221d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        }
222d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    }
223d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
224842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    public File createUniqueFileInternal(String dir, String filename) {
225842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        File directory;
226842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (dir == null) {
227842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory = mContext.getFilesDir();
228842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        } else {
229842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory = new File(dir);
230842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
231842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (!directory.exists()) {
232842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            directory.mkdirs();
233842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
234842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        File file = new File(directory, filename);
235842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (!file.exists()) {
236842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            return file;
237842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
238842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        // Get the extension of the file, if any.
239842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        int index = filename.lastIndexOf('.');
240842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        String name = filename;
241842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        String extension = "";
242842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        if (index != -1) {
243842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            name = filename.substring(0, index);
244842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            extension = filename.substring(index);
245842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
246842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        for (int i = 2; i < Integer.MAX_VALUE; i++) {
247842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            file = new File(directory, name + '-' + i + extension);
248842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            if (!file.exists()) {
249842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                return file;
250842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank            }
251842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        }
252842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank        return null;
253842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    }
254842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank
255d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank    /**
256d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * Loads an attachment, based on the PartRequest passed in.  The PartRequest is basically our
257d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * wrapper for Attachment
258d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * @param req the part (attachment) to be retrieved
259d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     * @throws IOException
260d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank     */
261842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank    protected void getAttachment(PartRequest req) throws IOException {
262d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        Attachment att = req.att;
263b0ce70f8d18dfe14fdd72be528d89eda1ba229feMarc Blank        Message msg = Message.restoreMessageWithId(mContext, att.mMessageKey);
26481d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank        doProgressCallback(msg.mId, att.mId, 0);
265ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        DefaultHttpClient client = new DefaultHttpClient();
266d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        String us = makeUriString("GetAttachment", "&AttachmentName=" + att.mLocation);
267ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HttpPost method = new HttpPost(URI.create(us));
268ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        method.setHeader("Authorization", mAuthString);
269ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
270ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HttpResponse res = client.execute(method);
271ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        int status = res.getStatusLine().getStatusCode();
272ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (status == HttpURLConnection.HTTP_OK) {
273ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            HttpEntity e = res.getEntity();
274ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int len = (int)e.getContentLength();
275ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String type = e.getContentType().getValue();
276ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            InputStream is = res.getEntity().getContent();
277bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler            File f = (req.destination != null)
278bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    ? new File(req.destination)
279bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    : createUniqueFileInternal(req.destination, att.mFileName);
280ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (f != null) {
281bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                // Ensure that the target directory exists
282bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                File destDir = f.getParentFile();
283bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                if (!destDir.exists()) {
284bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    destDir.mkdirs();
285bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                }
286ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                FileOutputStream os = new FileOutputStream(f);
287ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (len > 0) {
288ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    try {
289ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartRequest = req;
290ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartInputStream = is;
291ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        byte[] bytes = new byte[CHUNK_SIZE];
292ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        int length = len;
293ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        while (len > 0) {
294ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int n = (len > CHUNK_SIZE ? CHUNK_SIZE : len);
295ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int read = is.read(bytes, 0, n);
296ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            os.write(bytes, 0, read);
297ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            len -= read;
298d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                            int pct = ((length - len) * 100 / length);
29981d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank                            doProgressCallback(msg.mId, att.mId, pct);
300ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
301ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } finally {
302ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartRequest = null;
303ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mPendingPartInputStream = null;
304ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
305ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
306ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                os.flush();
307ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                os.close();
308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
309d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                // EmailProvider will throw an exception if we try to update an unsaved attachment
310d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                if (att.isSaved()) {
311bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    String contentUriString = (req.contentUriString != null)
312bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                            ? req.contentUriString
313bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                            : "file://" + f.getAbsolutePath();
314d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    ContentValues cv = new ContentValues();
315bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler                    cv.put(AttachmentColumns.CONTENT_URI, contentUriString);
316d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    cv.put(AttachmentColumns.MIME_TYPE, type);
317d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    att.update(mContext, cv);
31881d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank                    doStatusCallback(msg.mId, att.mId, EmailServiceStatus.SUCCESS);
319d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
320ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
321d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank        } else {
32281d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank            doStatusCallback(msg.mId, att.mId, EmailServiceStatus.MESSAGE_NOT_FOUND);
323ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
325ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
326147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank    @SuppressWarnings("deprecation")
3279d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank    private String makeUriString(String cmd, String extra) throws IOException {
328ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank         // Cache the authentication string and the command string
329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String safeUserName = URLEncoder.encode(mUserName);
330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (mAuthString == null) {
331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String cs = mUserName + ':' + mPassword;
332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mAuthString = "Basic " + Base64.encodeBytes(cs.getBytes());
333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId + "&DeviceType="
334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    + mDeviceType;
335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
336ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String us = (mSsl ? "https" : "http") + "://" + mHostAddress +
337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            "/Microsoft-Server-ActiveSync";
338ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (cmd != null) {
339ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            us += "?Cmd=" + cmd + mCmdString;
340ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
341ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (extra != null) {
342ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            us += extra;
343ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
344ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return us;
345ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
346ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3478047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private void setHeaders(HttpRequestBase method) {
3488047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("Authorization", mAuthString);
3498047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("MS-ASProtocolVersion", mProtocolVersion);
3508047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("Connection", "keep-alive");
3518047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setHeader("User-Agent", mDeviceType + '/' + Eas.VERSION);
3528047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
353ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3548047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private HttpClient getHttpClient(int timeout) {
3558047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpParams params = new BasicHttpParams();
356c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        HttpConnectionParams.setConnectionTimeout(params, 10*SECONDS);
3578047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpConnectionParams.setSoTimeout(params, timeout);
3588047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        return new DefaultHttpClient(params);
3598047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
360ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
3618047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    protected HttpResponse sendHttpClientPost(String cmd, byte[] bytes) throws IOException {
3628047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpClient client =
3638047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            getHttpClient(cmd.equals(PING_COMMAND) ? PING_COMMAND_TIMEOUT : COMMAND_TIMEOUT);
3648047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        String us = makeUriString(cmd, null);
3658047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpPost method = new HttpPost(URI.create(us));
3668047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        if (cmd.startsWith("SendMail&")) {
3678047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            method.setHeader("Content-Type", "message/rfc822");
3688047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        } else {
3698047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml");
370ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
3718047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        setHeaders(method);
3728047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        method.setEntity(new ByteArrayEntity(bytes));
373c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        synchronized(getSynchronizer()) {
374c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            mPendingPost = method;
375c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
376c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        try {
377c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            return client.execute(method);
378c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        } finally {
379c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            synchronized(getSynchronizer()) {
380c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                mPendingPost = null;
381c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            }
382c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
3838047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    }
3848047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
3858047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    protected HttpResponse sendHttpClientOptions() throws IOException {
3868047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpClient client = getHttpClient(COMMAND_TIMEOUT);
3878047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        String us = makeUriString("OPTIONS", null);
3888047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        HttpOptions method = new HttpOptions(URI.create(us));
3898047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        setHeaders(method);
3908047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        return client.execute(method);
391ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
392ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
393ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String getTargetCollectionClassFromCursor(Cursor c) {
394ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN);
395ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (type == Mailbox.TYPE_CONTACTS) {
396ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Contacts";
397ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else if (type == Mailbox.TYPE_CALENDAR) {
398ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Calendar";
399ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else {
400ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return "Email";
401ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
402ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
403ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
404ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /**
405ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * Performs FolderSync
406ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     *
407ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @throws IOException
408ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @throws EasParserException
409ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
410a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank    public void runAccountMailbox() throws IOException, EasParserException {
411fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        // Initialize exit status to success
412fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        mExitStatus = EmailServiceStatus.SUCCESS;
413ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
414fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            try {
415fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                SyncManager.callback()
416fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                    .syncMailboxListStatus(mAccount.mId, EmailServiceStatus.IN_PROGRESS, 0);
417fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            } catch (RemoteException e1) {
418fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                // Don't care if this fails
419fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
420fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
421ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mAccount.mSyncKey == null) {
422ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mAccount.mSyncKey = "0";
4231b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Account syncKey INIT to 0");
4249387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                ContentValues cv = new ContentValues();
4259387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey);
4269387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler                mAccount.update(mContext, cv);
427ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
428ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
4299d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            boolean firstSync = mAccount.mSyncKey.equals("0");
4309d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            if (firstSync) {
4311b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Initial FolderSync");
4321b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            }
4331b275b9408d5b856e2482fa3951827489e9585ccMarc Blank
434ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // When we first start up, change all ping mailboxes to push.
435ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ContentValues cv = new ContentValues();
4369387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler            cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
437ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
4389d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING,
4399d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    new String[] {Long.toString(mAccount.mId)}) > 0) {
4409d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                SyncManager.kick("change ping boxes to push");
441ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
442ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
4430f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            // Determine our protocol version, if we haven't already
4440f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            if (mAccount.mProtocolVersion == null) {
4451b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                userLog("Determine EAS protocol version");
4468047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                HttpResponse resp = sendHttpClientOptions();
4478047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                int code = resp.getStatusLine().getStatusCode();
4488047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                userLog("OPTIONS response: " + code);
4498047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                if (code == HttpURLConnection.HTTP_OK) {
4508047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    Header header = resp.getFirstHeader("ms-asprotocolversions");
4518047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    String versions = header.getValue();
4528047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    if (versions != null) {
4538047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        if (versions.contains("12.0")) {
4548047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                            mProtocolVersion = "12.0";
455ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
4568047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
4578047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        mAccount.mProtocolVersion = mProtocolVersion;
4588047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        userLog(versions);
4598047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        userLog("Using version " + mProtocolVersion);
460ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } else {
4618047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        errorLog("No protocol versions in OPTIONS response");
462ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        throw new IOException();
463ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
4648047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                } else {
4658047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    errorLog("OPTIONS command failed; throwing IOException");
4668047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    throw new IOException();
4670f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
4680f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank            }
4691b275b9408d5b856e2482fa3951827489e9585ccMarc Blank
47077424af660458104b732bdcb718874b17d0cab3aMarc Blank            // Change all pushable boxes to push when we start the account mailbox
47177424af660458104b732bdcb718874b17d0cab3aMarc Blank            if (mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH) {
47277424af660458104b732bdcb718874b17d0cab3aMarc Blank                cv = new ContentValues();
47377424af660458104b732bdcb718874b17d0cab3aMarc Blank                cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
47477424af660458104b732bdcb718874b17d0cab3aMarc Blank                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
47577424af660458104b732bdcb718874b17d0cab3aMarc Blank                        SyncManager.WHERE_IN_ACCOUNT_AND_PUSHABLE,
47677424af660458104b732bdcb718874b17d0cab3aMarc Blank                        new String[] {Long.toString(mAccount.mId)}) > 0) {
47777424af660458104b732bdcb718874b17d0cab3aMarc Blank                    userLog("Push account; set pushable boxes to push...");
47877424af660458104b732bdcb718874b17d0cab3aMarc Blank                }
47977424af660458104b732bdcb718874b17d0cab3aMarc Blank            }
48077424af660458104b732bdcb718874b17d0cab3aMarc Blank
48177424af660458104b732bdcb718874b17d0cab3aMarc Blank            while (!mStop) {
4828047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 userLog("Sending Account syncKey: " + mAccount.mSyncKey);
4838047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 Serializer s = new Serializer();
4848047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY)
48577424af660458104b732bdcb718874b17d0cab3aMarc Blank                     .text(mAccount.mSyncKey).end().end().done();
4868047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 HttpResponse resp = sendHttpClientPost("FolderSync", s.toByteArray());
4878047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 if (mStop) break;
4888047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 int code = resp.getStatusLine().getStatusCode();
4898047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 if (code == HttpURLConnection.HTTP_OK) {
4908047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     HttpEntity entity = resp.getEntity();
4918047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     int len = (int)entity.getContentLength();
4928047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     if (len > 0) {
4938047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         InputStream is = entity.getContent();
4948047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         // Returns true if we need to sync again
495f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank                         userLog("FolderSync, deviceId = " + mDeviceId);
4968047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         if (new FolderSyncParser(is, this).parse()) {
4978047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                             continue;
4988047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                         }
4998047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                     }
5008047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 } else if (code == HttpURLConnection.HTTP_UNAUTHORIZED ||
5010f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                        code == HttpURLConnection.HTTP_FORBIDDEN) {
5020f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    mExitStatus = AbstractSyncService.EXIT_LOGIN_FAILURE;
5030f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } else {
5040f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    userLog("FolderSync response error: " + code);
5050f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
506ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
5079d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                // Change all push/hold boxes to push
5089d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                cv = new ContentValues();
5099d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH);
5109d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                if (mContentResolver.update(Mailbox.CONTENT_URI, cv,
5119d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX,
5129d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        new String[] {Long.toString(mAccount.mId)}) > 0) {
5139d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                    userLog("Set push/hold boxes to push...");
5149d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                }
5159d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
5160f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                try {
5170f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    SyncManager.callback()
5189d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank                        .syncMailboxListStatus(mAccount.mId, mExitStatus, 0);
5190f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } catch (RemoteException e1) {
5200f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    // Don't care if this fails
5210f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
522fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
5230f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                // Wait for push notifications.
5240f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                String threadName = Thread.currentThread().getName();
5250f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                try {
5260f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    runPingLoop();
5270f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } catch (StaleFolderListException e) {
5280f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    // We break out if we get told about a stale folder list
5290f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    userLog("Ping interrupted; folder list requires sync...");
5300f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                } finally {
5310f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                    Thread.currentThread().setName(threadName);
5320f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank                }
533ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
5340f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank         } catch (IOException e) {
535fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // We catch this here to send the folder sync status callback
536fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // A folder sync failed callback will get sent from run()
537fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            try {
5384d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                if (!mStop) {
5394d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    SyncManager.callback()
5404d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                        .syncMailboxListStatus(mAccount.mId,
5414d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                                EmailServiceStatus.CONNECTION_ERROR, 0);
5424d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                }
543fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            } catch (RemoteException e1) {
544fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank                // Don't care if this fails
545fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
546ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            throw new IOException();
547ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
548ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
549ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
550c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank    void pushFallback(long mailboxId) {
551c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId);
552c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        ContentValues cv = new ContentValues();
553c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        if (mailbox.mType == Mailbox.TYPE_INBOX) {
554c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            cv.put(Mailbox.SYNC_INTERVAL, PING_FALLBACK_INBOX);
555c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            errorLog("*** PING LOOP: Turning off push due to ping loop on inbox...");
556ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            mContentResolver.update(Mailbox.CONTENT_URI, cv,
557ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    MailboxColumns.ACCOUNT_KEY + '=' + mAccount.mId
558ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    + AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX, null);
559ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            // Now, change the account as well
560ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            cv.clear();
561c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            cv.put(Account.SYNC_INTERVAL, PING_FALLBACK_INBOX);
562ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            mContentResolver.update(ContentUris.withAppendedId(Account.CONTENT_URI, mAccount.mId),
563ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    cv, null, null);
564c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
56577424af660458104b732bdcb718874b17d0cab3aMarc Blank            // Let the SyncManager know that something's changed
56677424af660458104b732bdcb718874b17d0cab3aMarc Blank            SyncManager.kick("push fallback");
56777424af660458104b732bdcb718874b17d0cab3aMarc Blank
568ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            // TODO Discuss the best way to alert the user
569ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            // Alert the user about what we've done
570ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            NotificationManager nm = (NotificationManager)mContext
571ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                .getSystemService(Context.NOTIFICATION_SERVICE);
572ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            Notification note =
573ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                new Notification(R.drawable.stat_notify_email_generic,
574ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        mContext.getString(R.string.notification_ping_loop_title),
575ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        System.currentTimeMillis());
576ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            Intent i = new Intent(mContext, AccountFolderList.class);
577ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            PendingIntent pi = PendingIntent.getActivity(mContext, 0, i, 0);
578ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            note.setLatestEventInfo(mContext,
579ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    mContext.getString(R.string.notification_ping_loop_title),
580ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    mContext.getString(R.string.notification_ping_loop_text), pi);
581ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank            nm.notify(Eas.EXCHANGE_ERROR_NOTIFICATION, note);
582c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        } else {
583c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            // Just change this box to sync every 10 minutes
584c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            cv.put(Mailbox.SYNC_INTERVAL, PING_FALLBACK_PIM);
585c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId),
586c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                    cv, null, null);
587c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            errorLog("*** PING LOOP: Backing off sync of " + mailbox.mDisplayName + " to 10 mins");
588c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        }
589368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
590368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
591ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    void runPingLoop() throws IOException, StaleFolderListException {
592ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // Do push for all sync services here
593368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        ArrayList<Mailbox> pushBoxes = new ArrayList<Mailbox>();
594c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank        long endTime = System.currentTimeMillis() + (30*MINUTES);
595ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank        HashMap<Long, Integer> pingFailureMap = new HashMap<Long, Integer>();
596ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
597ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        while (System.currentTimeMillis() < endTime) {
598ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Count of pushable mailboxes
599ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int pushCount = 0;
600ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Count of mailboxes that can be pushed right now
601ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int canPushCount = 0;
6027c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            Serializer s = new Serializer();
603ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int code;
604ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
60522bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                    MailboxColumns.ACCOUNT_KEY + '=' + mAccount.mId +
60622bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                    AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX, null, null);
607ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
608368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            pushBoxes.clear();
609368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
610ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            try {
611ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Loop through our pushed boxes seeing what is available to push
612ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                while (c.moveToNext()) {
613ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    pushCount++;
614ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // Two requirements for push:
615ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // 1) SyncManager tells us the mailbox is syncable (not running, not stopped)
616ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // 2) The syncKey isn't "0" (i.e. it's synced at least once)
617ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                    long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN);
61877424af660458104b732bdcb718874b17d0cab3aMarc Blank                    int pingStatus = SyncManager.pingStatus(mailboxId);
61977424af660458104b732bdcb718874b17d0cab3aMarc Blank                    String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN);
62077424af660458104b732bdcb718874b17d0cab3aMarc Blank                    if (pingStatus == SyncManager.PING_STATUS_OK) {
621ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN);
622ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        if (syncKey == null || syncKey.equals("0")) {
62377424af660458104b732bdcb718874b17d0cab3aMarc Blank                            pushCount--;
624ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            continue;
625ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
626ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank
627ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // Take a peek at this box's behavior last sync
628ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // We do this because some Exchange 2003 servers put themselves (and
629ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // therefore our client) into a "ping loop" in which the client is
630ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // continuously told of server changes, only to find that there aren't any.
631ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // This behavior is seemingly random, and we must code defensively by
632ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // backing off of push behavior when this is detected.
633ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // The server fix is at http://support.microsoft.com/kb/923282
634ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank
635ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        // Sync status is encoded as S<type>:<exitstatus>:<changes>
636ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        String status = c.getString(Mailbox.CONTENT_SYNC_STATUS_COLUMN);
637ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        int type = SyncManager.getStatusType(status);
638ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        if (type == SyncManager.SYNC_PING) {
639ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                            int changeCount = SyncManager.getStatusChangeCount(status);
640c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                            if (changeCount > 0) {
641c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                                pingFailureMap.remove(mailboxId);
642c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                            } else if (changeCount == 0) {
643ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                // This means that a ping failed; we'll keep track of this
644ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                Integer failures = pingFailureMap.get(mailboxId);
645ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                if (failures == null) {
646ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                    pingFailureMap.put(mailboxId, 1);
647ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                } else if (failures > 4) {
648ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                    // Change all push/ping boxes (except account) to 5 minute sync
649c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                                    pushFallback(mailboxId);
650ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                    return;
651ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                } else {
652ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                    pingFailureMap.put(mailboxId, failures + 1);
653ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                }
654ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                            }
655ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                        }
656ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank
657ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        if (canPushCount++ == 0) {
658ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            // Initialize the Ping command
6597c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            s.start(Tags.PING_PING).data(Tags.PING_HEARTBEAT_INTERVAL, "900")
6607c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                                .start(Tags.PING_FOLDERS);
661ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
662ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // When we're ready for Calendar/Contacts, we will check folder type
663ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // TODO Save Calendar and Contacts!! Mark as not visible!
664ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String folderClass = getTargetCollectionClassFromCursor(c);
6657c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                        s.start(Tags.PING_FOLDER)
6667c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .data(Tags.PING_ID, c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN))
6677c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .data(Tags.PING_CLASS, folderClass)
6687c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            .end();
66977424af660458104b732bdcb718874b17d0cab3aMarc Blank                        userLog("Ping ready for: " + folderClass + ", " + mailboxName + " (" +
67077424af660458104b732bdcb718874b17d0cab3aMarc Blank                                c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN) + ')');
671368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                        pushBoxes.add(new Mailbox().restore(c));
67277424af660458104b732bdcb718874b17d0cab3aMarc Blank                    } else if (pingStatus == SyncManager.PING_STATUS_RUNNING ||
67377424af660458104b732bdcb718874b17d0cab3aMarc Blank                            pingStatus == SyncManager.PING_STATUS_WAITING) {
67477424af660458104b732bdcb718874b17d0cab3aMarc Blank                        userLog(mailboxName + " not ready for ping");
67577424af660458104b732bdcb718874b17d0cab3aMarc Blank                    } else if (pingStatus == SyncManager.PING_STATUS_UNABLE) {
67677424af660458104b732bdcb718874b17d0cab3aMarc Blank                        pushCount--;
67777424af660458104b732bdcb718874b17d0cab3aMarc Blank                        userLog(mailboxName + " in error state; ignore");
67877424af660458104b732bdcb718874b17d0cab3aMarc Blank                        continue;
679ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
680ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
681ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } finally {
682ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                c.close();
683ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
684ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
68522bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank            if (canPushCount > 0 && (canPushCount == pushCount)) {
686ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // If we have some number that are ready for push, send Ping to the server
6877c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.end().end().done();
6887c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank
689d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                Thread.currentThread().setName(mAccount.mDisplayName + ": Ping");
690c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                userLog("Sending ping, timeout: " + PING_COMMAND_TIMEOUT / MINUTES + "m");
691f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank
692f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank                SyncManager.runAsleep(mMailboxId, PING_COMMAND_TIMEOUT);
693c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                long time = System.currentTimeMillis();
694f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank                HttpResponse res = sendHttpClientPost(PING_COMMAND, s.toByteArray());
695f708e075473f4c186c44b61bc5ad5c73c901b61eMarc Blank                SyncManager.runAwake(mMailboxId);
6968047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank
6974d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // Don't send request if we've been asked to stop
6984d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                if (mStop) return;
6998047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                code = res.getStatusLine().getStatusCode();
700368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
701c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                // Get elapsed time
702c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                time = System.currentTimeMillis() - time;
703c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                userLog("Ping response: " + code + " in " + time + "ms");
704c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
7054d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // Return immediately if we've been asked to stop
706842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                if (mStop) {
707368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                    userLog("Stopping pingLoop");
708842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                    return;
709842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                }
710368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
711ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (code == HttpURLConnection.HTTP_OK) {
7128047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    HttpEntity e = res.getEntity();
7138047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    int len = (int)e.getContentLength();
7148047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    InputStream is = res.getEntity().getContent();
7158047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                    if (len > 0) {
7168047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                        parsePingResult(is, mContentResolver);
717ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    } else {
718ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        throw new IOException();
719ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
720368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                } else if (isAuthError(code)) {
721ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    mExitStatus = AbstractSyncService.EXIT_LOGIN_FAILURE;
722ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    userLog("Authorization error during Ping: " + code);
723ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    throw new IOException();
724ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
725ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else if (pushCount > 0) {
726ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // If we want to Ping, but can't just yet, wait 10 seconds and try again
72722bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank                userLog("pingLoop waiting for " + (pushCount - canPushCount) + " box(es)");
728c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                sleep(10*SECONDS);
729ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
730c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                // We've got nothing to do, so we'll check again in 30 minutes at which time
731c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                // we'll update the folder list.
732c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                SyncManager.runAsleep(mMailboxId, 30*MINUTES);
733c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                sleep(30*MINUTES);
73477424af660458104b732bdcb718874b17d0cab3aMarc Blank                SyncManager.runAwake(mMailboxId);
735ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
736ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
737ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
738ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
739ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    void sleep(long ms) {
740ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
741ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Thread.sleep(ms);
742ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (InterruptedException e) {
743ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Doesn't matter whether we stop early; it's the thought that counts
744ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
745ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
746ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
7478047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank    private int parsePingResult(InputStream is, ContentResolver cr)
748ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        throws IOException, StaleFolderListException {
7498047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank        PingParser pp = new PingParser(is, this);
750ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (pp.parse()) {
751ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // True indicates some mailboxes need syncing...
752ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // syncList has the serverId's of the mailboxes...
753ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mBindArguments[0] = Long.toString(mAccount.mId);
754ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<String> syncList = pp.getSyncList();
755ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            for (String serverId: syncList) {
756d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                mBindArguments[1] = serverId;
757ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                Cursor c = cr.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION,
758ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        WHERE_ACCOUNT_KEY_AND_SERVER_ID, mBindArguments, null);
759ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                try {
760ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (c.moveToFirst()) {
7614d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                        SyncManager.startManualSync(c.getLong(Mailbox.CONTENT_ID_COLUMN),
762ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank                                SyncManager.SYNC_PING, null);
763ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
764ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } finally {
765ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    c.close();
766ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
767ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
768ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
7697c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank        return pp.getSyncList().size();
770ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
771ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
772ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    ByteArrayInputStream readResponse(HttpURLConnection uc) throws IOException {
773ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String encoding = uc.getHeaderField("Transfer-Encoding");
774ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (encoding == null) {
775ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int len = uc.getHeaderFieldInt("Content-Length", 0);
776ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (len > 0) {
777ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                InputStream in = uc.getInputStream();
778ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                byte[] bytes = new byte[len];
779ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                int remain = len;
780ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                int offs = 0;
781ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                while (remain > 0) {
782ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    int read = in.read(bytes, offs, remain);
783ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    remain -= read;
784ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    offs += read;
785ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
786ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return new ByteArrayInputStream(bytes);
787ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
788ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else if (encoding.equalsIgnoreCase("chunked")) {
789ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // TODO We don't handle this yet
790ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return null;
791ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
792ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return null;
793ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
794ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
795ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    String readResponseString(HttpURLConnection uc) throws IOException {
796ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String encoding = uc.getHeaderField("Transfer-Encoding");
797ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (encoding == null) {
798ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int len = uc.getHeaderFieldInt("Content-Length", 0);
799ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (len > 0) {
800ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                InputStream in = uc.getInputStream();
801ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                byte[] bytes = new byte[len];
802ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                int remain = len;
803ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                int offs = 0;
804ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                while (remain > 0) {
805ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    int read = in.read(bytes, offs, remain);
806ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    remain -= read;
807ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    offs += read;
808ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
809ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return new String(bytes);
810ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
811ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } else if (encoding.equalsIgnoreCase("chunked")) {
812ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // TODO We don't handle this yet
813ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return null;
814ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
815ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return null;
816ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
817ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
818368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    private String getFilterType() {
819368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        String filter = Eas.FILTER_1_WEEK;
820368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        switch (mAccount.mSyncLookback) {
821368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_DAY: {
822368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_DAY;
823368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
824368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
825368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_3_DAYS: {
826368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_3_DAYS;
827368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
828368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
829368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_WEEK: {
830368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_WEEK;
831368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
832368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
833368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_2_WEEKS: {
834368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_2_WEEKS;
835368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
836368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
837368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_1_MONTH: {
838368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_1_MONTH;
839368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
840368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
841368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            case com.android.email.Account.SYNC_WINDOW_ALL: {
842368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                filter = Eas.FILTER_ALL;
843368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                break;
844368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank            }
845368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        }
846368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank        return filter;
847368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    }
848368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank
849ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /**
850ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * Common code to sync E+PIM data
851ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     *
852ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @param target, an EasMailbox, EasContacts, or EasCalendar object
853ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
8547c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank    public void sync(AbstractSyncAdapter target) throws IOException {
855ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mTarget = target;
856ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        Mailbox mailbox = target.mMailbox;
857ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
858ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        boolean moreAvailable = true;
859ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        while (!mStop && moreAvailable) {
860ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            waitForConnectivity();
861ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
862d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            while (true) {
863d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                PartRequest req = null;
864d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                synchronized (mPartRequests) {
865d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    if (mPartRequests.isEmpty()) {
866d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        break;
867d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    } else {
868d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        req = mPartRequests.get(0);
869d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    }
870d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
871842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank                getAttachment(req);
872d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                synchronized(mPartRequests) {
873d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                    mPartRequests.remove(req);
874d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                }
875d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            }
876d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank
8777c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            Serializer s = new Serializer();
878ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (mailbox.mSyncKey == null) {
879ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Mailbox syncKey RESET");
880ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mailbox.mSyncKey = "0";
881ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
882ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String className = target.getCollectionName();
883ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            userLog("Sending " + className + " syncKey: " + mailbox.mSyncKey);
8847c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.start(Tags.SYNC_SYNC)
8857c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .start(Tags.SYNC_COLLECTIONS)
8867c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .start(Tags.SYNC_COLLECTION)
8877c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_CLASS, className)
8887c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_SYNC_KEY, mailbox.mSyncKey)
8897c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId)
8907c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                .tag(Tags.SYNC_DELETES_AS_MOVES);
891ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
892ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // EAS doesn't like GetChanges if the syncKey is "0"; not documented
893ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (!mailbox.mSyncKey.equals("0")) {
8947c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.tag(Tags.SYNC_GET_CHANGES);
895ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
8961b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            s.data(Tags.SYNC_WINDOW_SIZE,
8971b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                    className.equals("Email") ? EMAIL_WINDOW_SIZE : PIM_WINDOW_SIZE);
898ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            boolean options = false;
899ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (!className.equals("Contacts")) {
900ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Set the lookback appropriately (EAS calls this a "filter")
901368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                s.start(Tags.SYNC_OPTIONS).data(Tags.SYNC_FILTER_TYPE, getFilterType());
9027c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                // No truncation in this version
9037c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                //if (mProtocolVersionDouble < 12.0) {
9047c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                //    s.data(Tags.SYNC_TRUNCATION, "7");
9057c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                //}
90600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                options = true;
907ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
90800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            if (mProtocolVersionDouble >= 12.0) {
909ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (!options) {
910ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    options = true;
9117c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    s.start(Tags.SYNC_OPTIONS);
912ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
9137c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.start(Tags.BASE_BODY_PREFERENCE)
914368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                    // HTML for email; plain text for everything else
915c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank                    .data(Tags.BASE_TYPE, (className.equals("Email") ? Eas.BODY_PREFERENCE_HTML
916368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                            : Eas.BODY_PREFERENCE_TEXT))
917368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                // No truncation in this version
918368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                //.data(Tags.BASE_TRUNCATION_SIZE, Eas.DEFAULT_BODY_TRUNCATION_SIZE)
9197c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    .end();
920ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
921ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (options) {
9227c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                s.end();
923ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
924ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
925ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Send our changes up to the server
926ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            target.sendLocalChanges(s, this);
927ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
9287c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.end().end().end().done();
929c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            userLog("Sync, deviceId = " + mDeviceId);
9308047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            HttpResponse resp = sendHttpClientPost("Sync", s.toByteArray());
9318047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank            int code = resp.getStatusLine().getStatusCode();
932ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (code == HttpURLConnection.HTTP_OK) {
9338047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank                 InputStream is = resp.getEntity().getContent();
934ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (is != null) {
935ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    moreAvailable = target.parse(is, this);
9368480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank                    target.cleanup(this);
937ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
938ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
939ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                userLog("Sync response error: " + code);
940368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank                if (isAuthError(code)) {
941ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    mExitStatus = AbstractSyncService.EXIT_LOGIN_FAILURE;
942ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
943ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                return;
944ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
945ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
946ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
947ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
948ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    /* (non-Javadoc)
949ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     * @see java.lang.Runnable#run()
950ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank     */
951ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public void run() {
952ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mThread = Thread.currentThread();
953ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        TAG = mThread.getName();
9549d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
955ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        HostAuth ha = HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv);
956ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mHostAddress = ha.mAddress;
957ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mUserName = ha.mLogin;
958ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mPassword = ha.mPassword;
959ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
960fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        try {
961fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            SyncManager.callback().syncMailboxStatus(mMailboxId, EmailServiceStatus.IN_PROGRESS, 0);
962fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        } catch (RemoteException e1) {
963fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            // Don't care if this fails
964fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank        }
965fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank
966ab701c862d6e380c8e09ac53ed493aec5d523d3dMarc Blank        // Make sure account and mailbox are always the latest from the database
967ab701c862d6e380c8e09ac53ed493aec5d523d3dMarc Blank        mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
968ab701c862d6e380c8e09ac53ed493aec5d523d3dMarc Blank        mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId);
969a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank        // Whether or not we're the account mailbox
970ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
9719d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank            mDeviceId = SyncManager.getDeviceId();
972147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank            if (mMailbox == null || mAccount == null) {
973147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank                return;
9747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            } else if (mMailbox.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) {
975a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank                runAccountMailbox();
976ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } else {
9777c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                AbstractSyncAdapter target;
97800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mAccount = Account.restoreAccountWithId(mContext, mAccount.mId);
97900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mProtocolVersion = mAccount.mProtocolVersion;
98000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mProtocolVersionDouble = Double.parseDouble(mProtocolVersion);
981ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (mMailbox.mType == Mailbox.TYPE_CONTACTS)
9827c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    target = new ContactsSyncAdapter(mMailbox, this);
983ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                else {
9847c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    target = new EmailSyncAdapter(mMailbox, this);
985ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
986ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // We loop here because someone might have put a request in while we were syncing
987ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // and we've missed that opportunity...
988ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                do {
989ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (mRequestTime != 0) {
990ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        userLog("Looping for user request...");
991ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        mRequestTime = 0;
992ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
993ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    sync(target);
994ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } while (mRequestTime != 0);
995ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
996ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mExitStatus = EXIT_DONE;
997ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (IOException e) {
998ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            userLog("Caught IOException");
999ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mExitStatus = EXIT_IO_ERROR;
1000ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } catch (Exception e) {
100177424af660458104b732bdcb718874b17d0cab3aMarc Blank            Log.e(TAG, "Uncaught exception in EasSyncService", e);
1002ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } finally {
10034d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank            if (!mStop) {
10044d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                userLog(mMailbox.mDisplayName + ": sync finished");
10054d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                SyncManager.done(this);
10064d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // If this is the account mailbox, wake up SyncManager
10074d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // Because this box has a "push" interval, it will be restarted immediately
10084d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                // which will cause the folder list to be reloaded...
10095c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                int status;
10105c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                switch (mExitStatus) {
10115c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_IO_ERROR:
10125c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.CONNECTION_ERROR;
10135c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
10145c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_DONE:
10155c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.SUCCESS;
10165c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
10175c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    case EXIT_LOGIN_FAILURE:
10185c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.LOGIN_FAILED;
10195c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
10205c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                    default:
10215c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        status = EmailServiceStatus.REMOTE_EXCEPTION;
102277424af660458104b732bdcb718874b17d0cab3aMarc Blank                        errorLog("Sync ended due to an exception.");
10235c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        break;
10245c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                }
1025c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank
10264d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                try {
10274d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    SyncManager.callback().syncMailboxStatus(mMailboxId, status, 0);
10284d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                } catch (RemoteException e1) {
10294d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                    // Don't care if this fails
10304d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                }
10315c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank
10325c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                // Save the sync time and status
10335c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                ContentValues cv = new ContentValues();
10345c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
10355c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                String s = "S" + mSyncReason + ':' + status + ':' + mChangeCount;
10365c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                cv.put(Mailbox.SYNC_STATUS, s);
10375c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                mContentResolver.update(ContentUris
10385c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank                        .withAppendedId(Mailbox.CONTENT_URI, mMailboxId), cv, null, null);
10394d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank            } else {
10404d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank                userLog(mMailbox.mDisplayName + ": stopped thread finished.");
1041fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank            }
10429d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank
1043c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            // Make sure SyncManager knows about this
1044c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank            SyncManager.kick("sync finished");
10459d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank       }
1046ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
1047ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank}
1048