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