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