EasSyncService.java revision 5660b2e69436f2839de0659ada4880570c02b4a6
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 20f78833e76c1decf3a4a1371040a16205d1e59312Doug Zongkerimport com.android.email.SecurityPolicy; 218e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Predaimport com.android.email.Utility; 229c300767e8f6ab7a700aa1064791e75ff1a0bf3dMarc Blankimport com.android.email.SecurityPolicy.PolicySet; 235c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.email.mail.Address; 24ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.email.mail.AuthenticationFailedException; 255c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.email.mail.MeetingInfo; 26ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.email.mail.MessagingException; 275c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.email.mail.PackedString; 2867698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Account; 2967698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.AccountColumns; 3067698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Attachment; 3167698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.AttachmentColumns; 3267698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.HostAuth; 3367698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Mailbox; 3467698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.MailboxColumns; 3567698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Message; 36346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blankimport com.android.email.service.EmailServiceConstants; 374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport com.android.email.service.EmailServiceProxy; 3828d918b477d9a6ab790e872aa4170b1ae566cf42Makoto Onukiimport com.android.email.service.EmailServiceStatus; 397c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.AbstractSyncAdapter; 4048af7392c82262d17700e3fbdccf3a582809d449Marc Blankimport com.android.exchange.adapter.AccountSyncAdapter; 415862a85e17e81866ca82a9905577931947fbd44eMarc Blankimport com.android.exchange.adapter.CalendarSyncAdapter; 427c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.ContactsSyncAdapter; 437c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.EmailSyncAdapter; 447c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.FolderSyncParser; 4596bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadlerimport com.android.exchange.adapter.GalParser; 467531be7774769c84b499b1de5dc46da3a9468316Marc Blankimport com.android.exchange.adapter.MeetingResponseParser; 477c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.PingParser; 488692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blankimport com.android.exchange.adapter.ProvisionParser; 497c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.Serializer; 507c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.adapter.Tags; 514f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blankimport com.android.exchange.adapter.Parser.EasParserException; 5296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadlerimport com.android.exchange.provider.GalResult; 535c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.exchange.utility.CalendarUtilities; 54ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 558047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.Header; 5600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.HttpEntity; 5700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.HttpResponse; 581b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blankimport org.apache.http.HttpStatus; 598047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.HttpClient; 608047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.methods.HttpOptions; 6100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.client.methods.HttpPost; 628047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.client.methods.HttpRequestBase; 63e44d5875af006f4217718a1c0fc0e235af3863afMarc Blankimport org.apache.http.conn.ClientConnectionManager; 648047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.entity.ByteArrayEntity; 654d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.apache.http.entity.StringEntity; 6600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport org.apache.http.impl.client.DefaultHttpClient; 678047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.BasicHttpParams; 688047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.HttpConnectionParams; 698047ef058e41c164c2c8ab230ae8d123f042c167Marc Blankimport org.apache.http.params.HttpParams; 704d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.xmlpull.v1.XmlPullParser; 714d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.xmlpull.v1.XmlPullParserException; 724d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.xmlpull.v1.XmlPullParserFactory; 734d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport org.xmlpull.v1.XmlSerializer; 7400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 75ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentResolver; 76ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport android.content.ContentUris; 77ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentValues; 78ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.Context; 795c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport android.content.Entity; 80ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.database.Cursor; 814d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport android.os.Bundle; 82d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blankimport android.os.RemoteException; 8374c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blankimport android.os.SystemClock; 845c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport android.provider.Calendar.Attendees; 855c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport android.provider.Calendar.Events; 86b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blankimport android.text.TextUtils; 879972a855025df83129b9c7de4010024188219bd3Marc Blankimport android.util.Base64; 884d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport android.util.Log; 894d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport android.util.Xml; 90ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 914d14c368c1123394068c2f29caf7d3d04c56770aMarc Blankimport java.io.ByteArrayOutputStream; 9200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.File; 9300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.FileOutputStream; 9400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException; 9500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream; 965b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blankimport java.lang.Thread.State; 9700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URI; 9800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.net.URLEncoder; 99e44d5875af006f4217718a1c0fc0e235af3863afMarc Blankimport java.security.cert.CertificateException; 10000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList; 101ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankimport java.util.HashMap; 10200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 103ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blankpublic class EasSyncService extends AbstractSyncService { 104a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler // STOPSHIP - DO NOT RELEASE AS 'TRUE' 105a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler public static final boolean DEBUG_GAL_SERVICE = true; 106a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler 1071b275b9408d5b856e2482fa3951827489e9585ccMarc Blank private static final String EMAIL_WINDOW_SIZE = "5"; 108726d60d9b758f0383f8f8481190fc1a638427209Marc Blank public static final String PIM_WINDOW_SIZE = "5"; 109ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final String WHERE_ACCOUNT_KEY_AND_SERVER_ID = 110ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SERVER_ID + "=?"; 1119d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank private static final String WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING = 1129d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL + 113f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank '=' + Mailbox.CHECK_INTERVAL_PING; 11422bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank private static final String AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX = " AND " + 115f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank MailboxColumns.SYNC_INTERVAL + " IN (" + Mailbox.CHECK_INTERVAL_PING + 116f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank ',' + Mailbox.CHECK_INTERVAL_PUSH + ") AND " + MailboxColumns.TYPE + "!=\"" + 1177c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank Mailbox.TYPE_EAS_ACCOUNT_MAILBOX + '\"'; 1189d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank private static final String WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX = 1199d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank MailboxColumns.ACCOUNT_KEY + "=? and " + MailboxColumns.SYNC_INTERVAL + 120f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank '=' + Mailbox.CHECK_INTERVAL_PUSH_HOLD; 1218047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank static private final int CHUNK_SIZE = 16*1024; 1228047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank 1238047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank static private final String PING_COMMAND = "Ping"; 12416b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // Command timeout is the the time allowed for reading data from an open connection before an 12516b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // IOException is thrown. After a small added allowance, our watchdog alarm goes off (allowing 12616b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // us to detect a silently dropped connection). The allowance is defined below. 127c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank static private final int COMMAND_TIMEOUT = 20*SECONDS; 12816b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // Connection timeout is the time given to connect to the server before reporting an IOException 1299e029e58959e581637a1288a1ff9ef44cad63576Marc Blank static private final int CONNECTION_TIMEOUT = 20*SECONDS; 13016b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // The extra time allowed beyond the COMMAND_TIMEOUT before which our watchdog alarm triggers 1315660b2e69436f2839de0659ada4880570c02b4a6Marc Blank static private final int WATCHDOG_TIMEOUT_ALLOWANCE = 30*SECONDS; 132c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank 1334d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank static private final String AUTO_DISCOVER_SCHEMA_PREFIX = 1344d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank "http://schemas.microsoft.com/exchange/autodiscover/mobilesync/"; 1354d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank static private final String AUTO_DISCOVER_PAGE = "/autodiscover/autodiscover.xml"; 1364d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank static private final int AUTO_DISCOVER_REDIRECT_CODE = 451; 1374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 138b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank static public final String EAS_12_POLICY_TYPE = "MS-EAS-Provisioning-WBXML"; 139b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank static public final String EAS_2_POLICY_TYPE = "MS-WAP-Provisioning-XML"; 140b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank 1411b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank /** 1421b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * We start with an 8 minute timeout, and increase/decrease by 3 minutes at a time. There's 1431b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * no point having a timeout shorter than 5 minutes, I think; at that point, we can just let 1441b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * the ping exception out. The maximum I use is 17 minutes, which is really an empirical 1451b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * choice; too long and we risk silent connection loss and loss of push for that period. Too 1461b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * short and we lose efficiency/battery life. 1471b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * 1481b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * If we ever have to drop the ping timeout, we'll never increase it again. There's no point 1491b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * going into hysteresis; the NAT timeout isn't going to change without a change in connection, 1501b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * which will cause the sync service to be restarted at the starting heartbeat and going through 1511b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * the process again. 1521b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank */ 1531b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank static private final int PING_MINUTES = 60; // in seconds 1541b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank static private final int PING_FUDGE_LOW = 10; 1551b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank static private final int PING_STARTING_HEARTBEAT = (8*PING_MINUTES)-PING_FUDGE_LOW; 1561b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank static private final int PING_MIN_HEARTBEAT = (5*PING_MINUTES)-PING_FUDGE_LOW; 1571b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank static private final int PING_MAX_HEARTBEAT = (17*PING_MINUTES)-PING_FUDGE_LOW; 1581b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank static private final int PING_HEARTBEAT_INCREMENT = 3*PING_MINUTES; 1597672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank static private final int PING_FORCE_HEARTBEAT = 2*PING_MINUTES; 1601b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank 1611b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank static private final int PROTOCOL_PING_STATUS_COMPLETED = 1; 16296293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank 1635b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // The amount of time we allow for a thread to release its post lock after receiving an alert 1645b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank static private final int POST_LOCK_TIMEOUT = 10*SECONDS; 1655b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 166c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank // Fallbacks (in minutes) for ping loop failures 167252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank static private final int MAX_PING_FAILURES = 1; 168c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank static private final int PING_FALLBACK_INBOX = 5; 16927cf341571fac3d8dbe866f503c34fc31e02bf85Marc Blank static private final int PING_FALLBACK_PIM = 25; 170d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank 1718692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank // MSFT's custom HTTP result code indicating the need to provision 1728692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank static private final int HTTP_NEED_PROVISIONING = 449; 1738692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank 174ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Reasonable default 175d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank public String mProtocolVersion = Eas.DEFAULT_PROTOCOL_VERSION; 17600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank public Double mProtocolVersionDouble; 17785f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank protected String mDeviceId = null; 1789d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank private String mDeviceType = "Android"; 1791b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private String mAuthString = null; 1801b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private String mCmdString = null; 181ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String mHostAddress; 182ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String mUserName; 183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String mPassword; 1841b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private boolean mSsl = true; 1855843b85178a359446f81770ed7734604a1b2fa7dMarc Blank private boolean mTrustSsl = false; 186ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public ContentResolver mContentResolver; 1871b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private String[] mBindArguments = new String[2]; 1881b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private ArrayList<String> mPingChangeList; 1895b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // The HttpPost in progress 1905b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank private volatile HttpPost mPendingPost = null; 1911b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // The ping time (in seconds) 1921b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private int mPingHeartbeat = PING_STARTING_HEARTBEAT; 1931b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // The longest successful ping heartbeat 1941b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private int mPingHighWaterMark = 0; 1951b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // Whether we've ever lowered the heartbeat 1961b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank private boolean mPingHeartbeatDropped = false; 197e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // Whether a POST was aborted due to alarm (watchdog alarm) 198ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank private boolean mPostAborted = false; 199e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // Whether a POST was aborted due to reset 200e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank private boolean mPostReset = false; 201ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank // Whether or not the sync service is valid (usable) 202ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank public boolean mIsValid = true; 203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public EasSyncService(Context _context, Mailbox _mailbox) { 205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank super(_context, _mailbox); 206ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mContentResolver = _context.getContentResolver(); 207ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank if (mAccount == null) { 208ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank mIsValid = false; 209ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank return; 210ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank } 211ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank HostAuth ha = HostAuth.restoreHostAuthWithId(_context, mAccount.mHostAuthKeyRecv); 212ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank if (ha == null) { 213ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank mIsValid = false; 214ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank return; 215ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank } 216ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0; 217e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank mTrustSsl = (ha.mFlags & HostAuth.FLAG_TRUST_ALL_CERTIFICATES) != 0; 218ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 219ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 220ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private EasSyncService(String prefix) { 221ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank super(prefix); 222ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 223ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 224ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public EasSyncService() { 225ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank this("EAS Validation"); 226ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 227ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 228ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 2295b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank /** 2305b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * Try to wake up a sync thread that is waiting on an HttpClient POST and has waited past its 2315b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * socket timeout without having thrown an Exception 2325b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * 2335b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * @return true if the POST was successfully stopped; false if we've failed and interrupted 2345b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank * the thread 2355b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank */ 2365b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank public boolean alarm() { 2375b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank HttpPost post; 2385b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank String threadName = mThread.getName(); 2395b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 2405b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Synchronize here so that we are guaranteed to have valid mPendingPost and mPostLock 2415b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // executePostWithTimeout (which executes the HttpPost) also uses this lock 2421b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank synchronized(getSynchronizer()) { 2435b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Get a reference to the current post lock 2445b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank post = mPendingPost; 2455b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (post != null) { 2465b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (Eas.USER_LOG) { 2475b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank URI uri = post.getURI(); 2485b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (uri != null) { 2495b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank String query = uri.getQuery(); 2505b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (query == null) { 2515b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank query = "POST"; 2525b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2535b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank userLog(threadName, ": Alert, aborting ", query); 2545b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } else { 2555b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank userLog(threadName, ": Alert, no URI?"); 256e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 257e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 2585b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Abort the POST 259ddd7a7dabdd39feb3f769a3141eb4b0dd54db9e1Marc Blank mPostAborted = true; 2605b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank post.abort(); 261e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } else { 2625b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // If there's no POST, we're done 263e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank userLog("Alert, no pending POST"); 2645b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank return true; 2655b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2665b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2675b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 2685b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Wait for the POST to finish 2695b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank try { 2705b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank Thread.sleep(POST_LOCK_TIMEOUT); 2715b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } catch (InterruptedException e) { 2725b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2735b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 2745b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank State s = mThread.getState(); 2755b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if (Eas.USER_LOG) { 2765b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank userLog(threadName + ": State = " + s.name()); 2775b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank } 2785b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank 2795b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank synchronized (getSynchronizer()) { 2805b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // If the thread is still hanging around and the same post is pending, let's try to 2815b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // stop the thread with an interrupt. 2825b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank if ((s != State.TERMINATED) && (mPendingPost != null) && (mPendingPost == post)) { 2835b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank mStop = true; 2845b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank mThread.interrupt(); 2855b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank userLog("Interrupting..."); 2865b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Let the caller know we had to interrupt the thread 2875b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank return false; 288e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 289e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 2905b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank // Let the caller know that the alarm was handled normally 2915b4baf900a589e0ed00289631f6d0e1f85a95ecdMarc Blank return true; 292e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 293e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank 294e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank @Override 295e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank public void reset() { 296e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank synchronized(getSynchronizer()) { 297e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank if (mPendingPost != null) { 298e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank URI uri = mPendingPost.getURI(); 299e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank if (uri != null) { 300e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank String query = uri.getQuery(); 301e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank if (query.startsWith("Cmd=Ping")) { 302e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank userLog("Reset, aborting Ping"); 303e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank mPostReset = true; 304e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank mPendingPost.abort(); 305e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 306e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } 3071b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 309ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 310ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 311ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 312ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void stop() { 313ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mStop = true; 314c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank synchronized(getSynchronizer()) { 315c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank if (mPendingPost != null) { 316c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank mPendingPost.abort(); 317c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 318c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 3195c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank } 320ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 3211b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank /** 3221b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * Determine whether an HTTP code represents an authentication error 3231b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * @param code the HTTP code returned by the server 3241b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank * @return whether or not the code represents an authentication error 3251b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank */ 326f3ae2f9ee2ced1afc5cac4ebad125161726b6c0bMarc Blank protected boolean isAuthError(int code) { 3274d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return (code == HttpStatus.SC_UNAUTHORIZED) || (code == HttpStatus.SC_FORBIDDEN); 328368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 329368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank 33020da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 33120da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Determine whether an HTTP code represents a provisioning error 33220da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @param code the HTTP code returned by the server 33320da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @return whether or not the code represents an provisioning error 33420da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 33520da011530088036d2bf45d3836d6986a4b5d423Marc Blank protected boolean isProvisionError(int code) { 33620da011530088036d2bf45d3836d6986a4b5d423Marc Blank return (code == HTTP_NEED_PROVISIONING) || (code == HttpStatus.SC_FORBIDDEN); 33720da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 33820da011530088036d2bf45d3836d6986a4b5d423Marc Blank 339d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank private void setupProtocolVersion(EasSyncService service, Header versionHeader) 340d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank throws MessagingException { 341d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // The string is a comma separated list of EAS versions in ascending order 342d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // e.g. 1.0,2.0,2.5,12.0,12.1 343d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank String supportedVersions = versionHeader.getValue(); 344d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank userLog("Server supports versions: ", supportedVersions); 345d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank String[] supportedVersionsArray = supportedVersions.split(","); 346d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank String ourVersion = null; 347d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // Find the most recent version we support 348d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank for (String version: supportedVersionsArray) { 349d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (version.equals(Eas.SUPPORTED_PROTOCOL_EX2003) || 350d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank version.equals(Eas.SUPPORTED_PROTOCOL_EX2007)) { 351d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank ourVersion = version; 3528692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 353d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank } 354d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // If we don't support any of the servers supported versions, throw an exception here 355d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // This will cause validation to fail 356d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (ourVersion == null) { 357d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank Log.w(TAG, "No supported EAS versions: " + supportedVersions); 358d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank throw new MessagingException(MessagingException.PROTOCOL_VERSION_UNSUPPORTED); 359d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank } else { 360d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank service.mProtocolVersion = ourVersion; 361d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank service.mProtocolVersionDouble = Double.parseDouble(ourVersion); 3628692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank if (service.mAccount != null) { 363d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank service.mAccount.mProtocolVersion = ourVersion; 3648692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 3658692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 3668692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 3678692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank 368fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank @Override 369ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void validateAccount(String hostAddress, String userName, String password, int port, 370e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank boolean ssl, boolean trustCertificates, Context context) throws MessagingException { 371ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 3720a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Testing EAS: ", hostAddress, ", ", userName, ", ssl = ", ssl ? "1" : "0"); 373ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank EasSyncService svc = new EasSyncService("%TestAccount%"); 3749d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank svc.mContext = context; 375ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank svc.mHostAddress = hostAddress; 376ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank svc.mUserName = userName; 377ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank svc.mPassword = password; 378ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank svc.mSsl = ssl; 379e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank svc.mTrustSsl = trustCertificates; 380704cb199ced39727d84103c7170fc888a54f6c97Marc Blank // We mustn't use the "real" device id or we'll screw up current accounts 381704cb199ced39727d84103c7170fc888a54f6c97Marc Blank // Any string will do, but we'll go for "validate" 382704cb199ced39727d84103c7170fc888a54f6c97Marc Blank svc.mDeviceId = "validate"; 3838047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpResponse resp = svc.sendHttpClientOptions(); 3848047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank int code = resp.getStatusLine().getStatusCode(); 3859d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank userLog("Validation (OPTIONS) response: " + code); 3861b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (code == HttpStatus.SC_OK) { 387ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // No exception means successful validation 3883b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank Header commands = resp.getFirstHeader("MS-ASProtocolCommands"); 3893b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank Header versions = resp.getFirstHeader("ms-asprotocolversions"); 3903b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank if (commands == null || versions == null) { 3913b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank userLog("OPTIONS response without commands or versions; reporting I/O error"); 3923b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank throw new MessagingException(MessagingException.IOERROR); 3933b5688726987b9dbf020a35e0f80e3460fb0d838Marc Blank } 394eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank 3958692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank // Make sure we've got the right protocol version set up 3968692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank setupProtocolVersion(svc, versions); 3978692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank 398eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank // Run second test here for provisioning failures... 399eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank Serializer s = new Serializer(); 400eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank userLog("Try folder sync"); 401eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY).text("0") 402eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank .end().end().done(); 403eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank resp = svc.sendHttpClientPost("FolderSync", s.toByteArray()); 404eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank code = resp.getStatusLine().getStatusCode(); 4058692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank // We'll get one of the following responses if policies are required by the server 4068692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank if (code == HttpStatus.SC_FORBIDDEN || code == HTTP_NEED_PROVISIONING) { 4078692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank // Get the policies and see if we are able to support them 40820da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (svc.canProvision() != null) { 4098692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank // If so, send the advisory Exception (the account may be created later) 4108692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank throw new MessagingException(MessagingException.SECURITY_POLICIES_REQUIRED); 4118692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } else 4128692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank // If not, send the unsupported Exception (the account won't be created) 4138692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank throw new MessagingException( 4148692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank MessagingException.SECURITY_POLICIES_UNSUPPORTED); 4156e4eccde96c40935019800fb9034abbdce31aaf8Marc Blank } else if (code == HttpStatus.SC_NOT_FOUND) { 4166e4eccde96c40935019800fb9034abbdce31aaf8Marc Blank // We get a 404 from OWA addresses (which are NOT EAS addresses) 4176e4eccde96c40935019800fb9034abbdce31aaf8Marc Blank throw new MessagingException(MessagingException.PROTOCOL_VERSION_UNSUPPORTED); 4186e4eccde96c40935019800fb9034abbdce31aaf8Marc Blank } else if (code != HttpStatus.SC_OK) { 4196e4eccde96c40935019800fb9034abbdce31aaf8Marc Blank // Fail generically with anything other than success 4206e4eccde96c40935019800fb9034abbdce31aaf8Marc Blank userLog("Unexpected response for FolderSync: ", code); 4216e4eccde96c40935019800fb9034abbdce31aaf8Marc Blank throw new MessagingException(MessagingException.UNSPECIFIED_EXCEPTION); 422eb9517c855a275880ac2cd4dbcca0d0a37b70567Marc Blank } 423ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank userLog("Validation successful"); 424ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return; 425ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 426368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank if (isAuthError(code)) { 427ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank userLog("Authentication failed"); 428ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new AuthenticationFailedException("Validation failed"); 429ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 430ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // TODO Need to catch other kinds of errors (e.g. policy) For now, report the code. 4310a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Validation failed, reporting I/O error: ", code); 432ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new MessagingException(MessagingException.IOERROR); 433ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 434ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (IOException e) { 435e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank Throwable cause = e.getCause(); 436e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank if (cause != null && cause instanceof CertificateException) { 437e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank userLog("CertificateException caught: ", e.getMessage()); 438e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank throw new MessagingException(MessagingException.GENERAL_SECURITY); 439e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank } 440e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank userLog("IOException caught: ", e.getMessage()); 441ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new MessagingException(MessagingException.IOERROR); 442ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 443ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 444ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 445ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 4464d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank /** 4474d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * Gets the redirect location from the HTTP headers and uses that to modify the HttpPost so that 4484d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * it can be reused 4494d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * 4504d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param resp the HttpResponse that indicates a redirect (451) 4514d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param post the HttpPost that was originally sent to the server 4524d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @return the HttpPost, updated with the redirect location 4534d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank */ 4544d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank private HttpPost getRedirect(HttpResponse resp, HttpPost post) { 4554d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank Header locHeader = resp.getFirstHeader("X-MS-Location"); 4564d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (locHeader != null) { 4574d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String loc = locHeader.getValue(); 4584d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // If we've gotten one and it shows signs of looking like an address, we try 4594d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // sending our request there 4604d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (loc != null && loc.startsWith("http")) { 4614d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post.setURI(URI.create(loc)); 4624d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return post; 4634d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 4644d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 4654d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return null; 4664d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 4674d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 4684d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank /** 4694d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * Send the POST command to the autodiscover server, handling a redirect, if necessary, and 4704d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * return the HttpResponse 4714d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * 4724d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param client the HttpClient to be used for the request 4734d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param post the HttpPost we're going to send 4744d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @return an HttpResponse from the original or redirect server 4754d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @throws IOException on any IOException within the HttpClient code 4764d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @throws MessagingException 4774d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank */ 4784d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank private HttpResponse postAutodiscover(HttpClient client, HttpPost post) 4794d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws IOException, MessagingException { 4804d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Posting autodiscover to: " + post.getURI()); 481adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank HttpResponse resp = executePostWithTimeout(client, post, COMMAND_TIMEOUT); 4824d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int code = resp.getStatusLine().getStatusCode(); 4834d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // On a redirect, try the new location 4844d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (code == AUTO_DISCOVER_REDIRECT_CODE) { 4854d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post = getRedirect(resp, post); 4864d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (post != null) { 4874d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Posting autodiscover to redirect: " + post.getURI()); 488adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank return executePostWithTimeout(client, post, COMMAND_TIMEOUT); 4894d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 49052f7f7effdd320594cd1c5c8c67443ad51eca3afMarc Blank } else if (code == HttpStatus.SC_UNAUTHORIZED) { 49152f7f7effdd320594cd1c5c8c67443ad51eca3afMarc Blank // 401 (Unauthorized) is for true auth errors when used in Autodiscover 49252f7f7effdd320594cd1c5c8c67443ad51eca3afMarc Blank // 403 (and others) we'll just punt on 4934d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throw new MessagingException(MessagingException.AUTHENTICATION_FAILED); 4944d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (code != HttpStatus.SC_OK) { 4954d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We'll try the next address if this doesn't work 4964d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Code: " + code + ", throwing IOException"); 4974d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throw new IOException(); 4984d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 4994d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return resp; 5004d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 5014d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 5024d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank /** 5034d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * Use the Exchange 2007 AutoDiscover feature to try to retrieve server information using 5044d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * only an email address and the password 5054d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * 5064d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param userName the user's email address 5074d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @param password the user's password 5084d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank * @return a HostAuth ready to be saved in an Account or null (failure) 5094d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank */ 5104d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank public Bundle tryAutodiscover(String userName, String password) throws RemoteException { 5114d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank XmlSerializer s = Xml.newSerializer(); 5124d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank ByteArrayOutputStream os = new ByteArrayOutputStream(1024); 5134d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HostAuth hostAuth = new HostAuth(); 5144d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank Bundle bundle = new Bundle(); 5154d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 5164d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.NO_ERROR); 5174d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank try { 5184d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Build the XML document that's sent to the autodiscover server(s) 5194d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.setOutput(os, "UTF-8"); 5204d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startDocument("UTF-8", false); 5214d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startTag(null, "Autodiscover"); 5224d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.attribute(null, "xmlns", AUTO_DISCOVER_SCHEMA_PREFIX + "requestschema/2006"); 5234d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startTag(null, "Request"); 5244d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startTag(null, "EMailAddress").text(userName).endTag(null, "EMailAddress"); 5254d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.startTag(null, "AcceptableResponseSchema"); 5264d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.text(AUTO_DISCOVER_SCHEMA_PREFIX + "responseschema/2006"); 5274d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.endTag(null, "AcceptableResponseSchema"); 5284d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.endTag(null, "Request"); 5294d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.endTag(null, "Autodiscover"); 5304d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank s.endDocument(); 5314d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String req = os.toString(); 5324d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 5334d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Initialize the user name and password 5344d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank mUserName = userName; 5354d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank mPassword = password; 5364d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Make sure the authentication string is created (mAuthString) 5374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank makeUriString("foo", null); 5384d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 5394d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Split out the domain name 5404d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int amp = userName.indexOf('@'); 5414d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // The UI ensures that userName is a valid email address 5424d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (amp < 0) { 5434d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throw new RemoteException(); 5444d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 5454d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String domain = userName.substring(amp + 1); 5464d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 5474d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // There are up to four attempts here; the two URLs that we're supposed to try per the 5484d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // specification, and up to one redirect for each (handled in postAutodiscover) 5494d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 5504d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Try the domain first and see if we can get a response 5514d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HttpPost post = new HttpPost("https://" + domain + AUTO_DISCOVER_PAGE); 55220da011530088036d2bf45d3836d6986a4b5d423Marc Blank setHeaders(post, false); 5534d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post.setHeader("Content-Type", "text/xml"); 5544d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post.setEntity(new StringEntity(req)); 5554d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HttpClient client = getHttpClient(COMMAND_TIMEOUT); 5564d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HttpResponse resp; 5574d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank try { 5584d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank resp = postAutodiscover(client, post); 5594d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (IOException e1) { 560adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank userLog("IOException in autodiscover; trying alternate address"); 5614d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We catch the IOException here because we have an alternate address to try 5624d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank post.setURI(URI.create("https://autodiscover." + domain + AUTO_DISCOVER_PAGE)); 5634d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // If we fail here, we're out of options, so we let the outer try catch the 5644d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // IOException and return null 5654d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank resp = postAutodiscover(client, post); 5664d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 5674d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 5684d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Get the "final" code; if it's not 200, just return null 5694d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int code = resp.getStatusLine().getStatusCode(); 5704d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Code: " + code); 5714d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (code != HttpStatus.SC_OK) return null; 5724d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 5734d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // At this point, we have a 200 response (SC_OK) 5744d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HttpEntity e = resp.getEntity(); 5754d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank InputStream is = e.getContent(); 5764d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank try { 5774d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // The response to Autodiscover is regular XML (not WBXML) 5784d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // If we ever get an error in this process, we'll just punt and return null 5794d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 5804d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank XmlPullParser parser = factory.newPullParser(); 5814d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parser.setInput(is, "UTF-8"); 5824d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.getEventType(); 5834d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.START_DOCUMENT) { 5844d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank type = parser.next(); 5854d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.START_TAG) { 5864d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 5874d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("Autodiscover")) { 5884d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth = new HostAuth(); 5894d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseAutodiscover(parser, hostAuth); 5904d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // On success, we'll have a server address and login 5914d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (hostAuth.mAddress != null && hostAuth.mLogin != null) { 5924d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Fill in the rest of the HostAuth 5934d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth.mPassword = password; 5944d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth.mPort = 443; 5954d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth.mProtocol = "eas"; 5964d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth.mFlags = 5974d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank HostAuth.FLAG_SSL | HostAuth.FLAG_AUTHENTICATE; 5984d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putParcelable( 5994d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank EmailServiceProxy.AUTO_DISCOVER_BUNDLE_HOST_AUTH, hostAuth); 6004d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else { 6014d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 6024d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.UNSPECIFIED_EXCEPTION); 6034d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6044d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6054d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6064d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6074d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (XmlPullParserException e1) { 6084d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // This would indicate an I/O error of some sort 6094d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We will simply return null and user can configure manually 6104d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6114d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // There's no reason at all for exceptions to be thrown, and it's ok if so. 6124d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We just won't do auto-discover; user can configure manually 6134d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (IllegalArgumentException e) { 6144d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 6154d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.UNSPECIFIED_EXCEPTION); 6164d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (IllegalStateException e) { 6174d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 6184d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.UNSPECIFIED_EXCEPTION); 6194d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (IOException e) { 6204d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("IOException in Autodiscover", e); 6214d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 6224d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.IOERROR); 6234d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } catch (MessagingException e) { 6244d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank bundle.putInt(EmailServiceProxy.AUTO_DISCOVER_BUNDLE_ERROR_CODE, 6254d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank MessagingException.AUTHENTICATION_FAILED); 6264d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6274d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank return bundle; 6284d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6294d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 6304d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseServer(XmlPullParser parser, HostAuth hostAuth) 6314d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 6324d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank boolean mobileSync = false; 6334d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 6344d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 6354d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Server")) { 6364d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 6374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 6384d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 6394d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("Type")) { 6404d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (parser.nextText().equals("MobileSync")) { 6414d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank mobileSync = true; 6424d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6434d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (mobileSync && name.equals("Url")) { 6444d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String url = parser.nextText().toLowerCase(); 6454d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // This will look like https://<server address>/Microsoft-Server-ActiveSync 6464d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // We need to extract the <server address> 6474d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (url.startsWith("https://") && 6484d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank url.endsWith("/microsoft-server-activesync")) { 6494d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int lastSlash = url.lastIndexOf('/'); 6504d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth.mAddress = url.substring(8, lastSlash); 6514d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Autodiscover, server: " + hostAuth.mAddress); 6524d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6534d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6544d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6554d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6564d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6574d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 6584d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseSettings(XmlPullParser parser, HostAuth hostAuth) 6594d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 6604d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 6614d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 6624d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Settings")) { 6634d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 6644d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 6654d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 6664d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("Server")) { 6674d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseServer(parser, hostAuth); 6684d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6694d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6704d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6714d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6724d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 6734d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseAction(XmlPullParser parser, HostAuth hostAuth) 6744d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 6754d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 6764d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 6774d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Action")) { 6784d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 6794d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 6804d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 6814d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("Error")) { 6824d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank // Should parse the error 6834d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (name.equals("Redirect")) { 6844d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank Log.d(TAG, "Redirect: " + parser.nextText()); 6854d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (name.equals("Settings")) { 6864d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseSettings(parser, hostAuth); 6874d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6884d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6894d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6904d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 6914d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 6924d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseUser(XmlPullParser parser, HostAuth hostAuth) 6934d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 6944d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 6954d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 6964d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("User")) { 6974d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 6984d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 6994d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 7004d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("EMailAddress")) { 7014d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String addr = parser.nextText(); 7024d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank hostAuth.mLogin = addr; 7034d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Autodiscover, login: " + addr); 7044d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (name.equals("DisplayName")) { 7054d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String dn = parser.nextText(); 7064d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Autodiscover, user: " + dn); 7074d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7084d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7094d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7104d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7114d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 7124d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseResponse(XmlPullParser parser, HostAuth hostAuth) 7134d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 7144d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 7154d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.next(); 7164d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Response")) { 7174d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 7184d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG) { 7194d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank String name = parser.getName(); 7204d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (name.equals("User")) { 7214d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseUser(parser, hostAuth); 7224d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (name.equals("Action")) { 7234d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseAction(parser, hostAuth); 7244d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7254d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7264d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7274d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7284d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 7294d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank void parseAutodiscover(XmlPullParser parser, HostAuth hostAuth) 7304d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank throws XmlPullParserException, IOException { 7314d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank while (true) { 7324d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int type = parser.nextTag(); 7334d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank if (type == XmlPullParser.END_TAG && parser.getName().equals("Autodiscover")) { 7344d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank break; 7354d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } else if (type == XmlPullParser.START_TAG && parser.getName().equals("Response")) { 7364d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank parseResponse(parser, hostAuth); 7374d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7384d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7394d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank } 7404d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank 74196bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler /** 74296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * Contact the GAL and obtain a list of matching accounts 74396bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * @param context caller's context 74496bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * @param accountId the account Id to search 74596bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * @param filter the characters entered so far 74696bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * @return a result record 74796bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * 74896bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * TODO: shorter timeout for interactive lookup 74996bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * TODO: make watchdog actually work (it doesn't understand our service w/Mailbox == 0) 75096bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler * TODO: figure out why sendHttpClientPost() hangs - possibly pool exhaustion 75196bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler */ 75296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler static public GalResult searchGal(Context context, long accountId, String filter) 75396bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler { 754114f17e8efea93380680417af4503c8e6820c394Marc Blank Account acct = SyncManager.getAccountById(accountId); 75596bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler if (acct != null) { 75696bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler HostAuth ha = HostAuth.restoreHostAuthWithId(context, acct.mHostAuthKeyRecv); 757a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler EasSyncService svc = new EasSyncService("%GalLookupk%"); 75896bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler try { 75996bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler svc.mContext = context; 76096bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler svc.mHostAddress = ha.mAddress; 76196bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler svc.mUserName = ha.mLogin; 76296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler svc.mPassword = ha.mPassword; 76396bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler svc.mSsl = (ha.mFlags & HostAuth.FLAG_SSL) != 0; 76496bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler svc.mTrustSsl = (ha.mFlags & HostAuth.FLAG_TRUST_ALL_CERTIFICATES) != 0; 76596bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler svc.mDeviceId = SyncManager.getDeviceId(); 7667b377bf23e53f2fda5c7c0f19ddf2f9d1096945eMarc Blank svc.mAccount = acct; 76796bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler Serializer s = new Serializer(); 76896bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler s.start(Tags.SEARCH_SEARCH).start(Tags.SEARCH_STORE); 76996bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler s.data(Tags.SEARCH_NAME, "GAL").data(Tags.SEARCH_QUERY, filter); 77096bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler s.start(Tags.SEARCH_OPTIONS); 771a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler s.data(Tags.SEARCH_RANGE, "0-19"); // Return 0..20 results 77296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler s.end().end().end().done(); 773a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler if (DEBUG_GAL_SERVICE) svc.userLog("GAL lookup starting for " + ha.mAddress); 77496bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler HttpResponse resp = svc.sendHttpClientPost("Search", s.toByteArray()); 77596bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler int code = resp.getStatusLine().getStatusCode(); 77696bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler if (code == HttpStatus.SC_OK) { 77796bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler InputStream is = resp.getEntity().getContent(); 77896bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler GalParser gp = new GalParser(is, svc); 77996bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler if (gp.parse()) { 780a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler if (DEBUG_GAL_SERVICE) svc.userLog("GAL lookup OK for " + ha.mAddress); 78196bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler return gp.getGalResult(); 782a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler } else { 783a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler if (DEBUG_GAL_SERVICE) svc.userLog("GAL lookup returned no matches"); 78496bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 785a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler } else { 786a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler svc.userLog("GAL lookup returned " + code); 78796bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 78896bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } catch (IOException e) { 78996bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler // GAL is non-critical; we'll just go on 790a6bcdcbd31fc4a07ccba54e74ac4ca7e476262feAndrew Stadler svc.userLog("GAL lookup exception " + e); 79196bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 79296bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 79396bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler return null; 79496bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler } 79596bdc2bfdd4d316259380dfba37c4d22dab7aaa0Andrew Stadler 79681d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank private void doStatusCallback(long messageId, long attachmentId, int status) { 797d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank try { 798fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank SyncManager.callback().loadAttachmentStatus(messageId, attachmentId, status, 0); 799fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } catch (RemoteException e) { 800d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank // No danger if the client is no longer around 801d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 802d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 803ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 80481d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank private void doProgressCallback(long messageId, long attachmentId, int progress) { 805d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank try { 806fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank SyncManager.callback().loadAttachmentStatus(messageId, attachmentId, 807fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank EmailServiceStatus.IN_PROGRESS, progress); 808fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } catch (RemoteException e) { 809d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank // No danger if the client is no longer around 810d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 811d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 812d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank 813842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank public File createUniqueFileInternal(String dir, String filename) { 814842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank File directory; 815842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank if (dir == null) { 816842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank directory = mContext.getFilesDir(); 817842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank } else { 818842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank directory = new File(dir); 819842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank } 820842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank if (!directory.exists()) { 821842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank directory.mkdirs(); 822842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank } 823842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank File file = new File(directory, filename); 824842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank if (!file.exists()) { 825842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank return file; 826842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank } 827842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank // Get the extension of the file, if any. 828842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank int index = filename.lastIndexOf('.'); 829842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank String name = filename; 830842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank String extension = ""; 831842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank if (index != -1) { 832842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank name = filename.substring(0, index); 833842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank extension = filename.substring(index); 834842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank } 835842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank for (int i = 2; i < Integer.MAX_VALUE; i++) { 836842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank file = new File(directory, name + '-' + i + extension); 837842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank if (!file.exists()) { 838842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank return file; 839842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank } 840842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank } 841842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank return null; 842842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank } 843842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank 844d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank /** 845d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank * Loads an attachment, based on the PartRequest passed in. The PartRequest is basically our 846d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank * wrapper for Attachment 847d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank * @param req the part (attachment) to be retrieved 848d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank * @throws IOException 849d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank */ 850842515bc55b31ad9034c4048ebb402248a69d18aMarc Blank protected void getAttachment(PartRequest req) throws IOException { 8517531be7774769c84b499b1de5dc46da3a9468316Marc Blank Attachment att = req.mAttachment; 852b0ce70f8d18dfe14fdd72be528d89eda1ba229feMarc Blank Message msg = Message.restoreMessageWithId(mContext, att.mMessageKey); 85381d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank doProgressCallback(msg.mId, att.mId, 0); 854ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 85542f47790b84483b15fdf4c7f53b283dd1d56d3faMarc Blank String cmd = "GetAttachment&AttachmentName=" + att.mLocation; 85642f47790b84483b15fdf4c7f53b283dd1d56d3faMarc Blank HttpResponse res = sendHttpClientPost(cmd, null, COMMAND_TIMEOUT); 85742f47790b84483b15fdf4c7f53b283dd1d56d3faMarc Blank 858ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int status = res.getStatusLine().getStatusCode(); 8591b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (status == HttpStatus.SC_OK) { 860ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank HttpEntity e = res.getEntity(); 861ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int len = (int)e.getContentLength(); 862ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank InputStream is = res.getEntity().getContent(); 8637531be7774769c84b499b1de5dc46da3a9468316Marc Blank File f = (req.mDestination != null) 8647531be7774769c84b499b1de5dc46da3a9468316Marc Blank ? new File(req.mDestination) 8657531be7774769c84b499b1de5dc46da3a9468316Marc Blank : createUniqueFileInternal(req.mDestination, att.mFileName); 866ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (f != null) { 867bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler // Ensure that the target directory exists 868bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler File destDir = f.getParentFile(); 869bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler if (!destDir.exists()) { 870bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler destDir.mkdirs(); 871bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler } 872ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank FileOutputStream os = new FileOutputStream(f); 87395e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // len > 0 means that Content-Length was set in the headers 87495e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // len < 0 means "chunked" transfer-encoding 87595e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank if (len != 0) { 876ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 8777531be7774769c84b499b1de5dc46da3a9468316Marc Blank mPendingRequest = req; 878ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank byte[] bytes = new byte[CHUNK_SIZE]; 879ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int length = len; 88095e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // Loop terminates 1) when EOF is reached or 2) if an IOException occurs 88195e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // One of these is guaranteed to occur 88295e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank int totalRead = 0; 88395e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank userLog("Attachment content-length: ", len); 88495e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank while (true) { 88595e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank int read = is.read(bytes, 0, CHUNK_SIZE); 88695e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank 88795e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // read < 0 means that EOF was reached 88895e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank if (read < 0) { 88995e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank userLog("Attachment load reached EOF, totalRead: ", totalRead); 89095e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank break; 89195e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank } 89295e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank 89395e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // Keep track of how much we've read for progress callback 89495e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank totalRead += read; 89595e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank 89695e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // Write these bytes out 897ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank os.write(bytes, 0, read); 89895e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank 89995e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // We can't report percentages if this is chunked; by definition, the 90095e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // length of incoming data is unknown 90195e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank if (length > 0) { 90295e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank // Belt and suspenders check to prevent runaway reading 90395e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank if (totalRead > length) { 90495e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank errorLog("totalRead is greater than attachment length?"); 90595e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank break; 90695e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank } 9074d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank int pct = (totalRead * 100) / length; 90895e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank doProgressCallback(msg.mId, att.mId, pct); 90995e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank } 91095e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank } 911ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 9127531be7774769c84b499b1de5dc46da3a9468316Marc Blank mPendingRequest = null; 913ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 914ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 915ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank os.flush(); 916ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank os.close(); 917ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 918d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank // EmailProvider will throw an exception if we try to update an unsaved attachment 919d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank if (att.isSaved()) { 9207531be7774769c84b499b1de5dc46da3a9468316Marc Blank String contentUriString = (req.mContentUriString != null) 9217531be7774769c84b499b1de5dc46da3a9468316Marc Blank ? req.mContentUriString 922bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler : "file://" + f.getAbsolutePath(); 923d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank ContentValues cv = new ContentValues(); 924bb7360cbcfa12694ebd8a842f8d1d25fc6897dfdAndrew Stadler cv.put(AttachmentColumns.CONTENT_URI, contentUriString); 925d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank att.update(mContext, cv); 92681d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank doStatusCallback(msg.mId, att.mId, EmailServiceStatus.SUCCESS); 927d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 928ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 929d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } else { 93081d9179a5bd856c39ae74f591983bf662d99fb05Marc Blank doStatusCallback(msg.mId, att.mId, EmailServiceStatus.MESSAGE_NOT_FOUND); 931ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 932ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 933ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 9347531be7774769c84b499b1de5dc46da3a9468316Marc Blank /** 9355c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * Send an email responding to a Message that has been marked as a meeting request. The message 9365c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * will consist a little bit of event information and an iCalendar attachment 9375c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @param msg the meeting request email 9385c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank */ 939346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank private void sendMeetingResponseMail(Message msg, int response) { 9405c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Get the meeting information; we'd better have some... 9415c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank PackedString meetingInfo = new PackedString(msg.mMeetingInfo); 9425c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (meetingInfo == null) return; 9435c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 9445c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // This will come as "First Last" <box@server.blah>, so we use Address to 9455c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // parse it into parts; we only need the email address part for the ics file 9465c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Address[] addrs = Address.parse(meetingInfo.get(MeetingInfo.MEETING_ORGANIZER_EMAIL)); 9475c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // It shouldn't be possible, but handle it anyway 9485c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (addrs.length != 1) return; 9495c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank String organizerEmail = addrs[0].getAddress(); 9505c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 9515c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank String dtStamp = meetingInfo.get(MeetingInfo.MEETING_DTSTAMP); 9525c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank String dtStart = meetingInfo.get(MeetingInfo.MEETING_DTSTART); 9535c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank String dtEnd = meetingInfo.get(MeetingInfo.MEETING_DTEND); 9545c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 9555c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // What we're doing here is to create an Entity that looks like an Event as it would be 9565c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // stored by CalendarProvider 9575c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank ContentValues entityValues = new ContentValues(); 9585c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Entity entity = new Entity(entityValues); 9595c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 9605c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Fill in times, location, title, and organizer 9615c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entityValues.put("DTSTAMP", 9625c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank CalendarUtilities.convertEmailDateTimeToCalendarDateTime(dtStamp)); 9638e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda entityValues.put(Events.DTSTART, Utility.parseEmailDateTimeToMillis(dtStart)); 9648e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda entityValues.put(Events.DTEND, Utility.parseEmailDateTimeToMillis(dtEnd)); 9655c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entityValues.put(Events.EVENT_LOCATION, meetingInfo.get(MeetingInfo.MEETING_LOCATION)); 9665c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entityValues.put(Events.TITLE, meetingInfo.get(MeetingInfo.MEETING_TITLE)); 9675c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entityValues.put(Events.ORGANIZER, organizerEmail); 9685c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 9695c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Add ourselves as an attendee, using our account email address 9705c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank ContentValues attendeeValues = new ContentValues(); 9715c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank attendeeValues.put(Attendees.ATTENDEE_RELATIONSHIP, 9725c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Attendees.RELATIONSHIP_ATTENDEE); 9735c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank attendeeValues.put(Attendees.ATTENDEE_EMAIL, mAccount.mEmailAddress); 9745c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entity.addSubValue(Attendees.CONTENT_URI, attendeeValues); 9755c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 9765c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Add the organizer 9775c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank ContentValues organizerValues = new ContentValues(); 9785c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank organizerValues.put(Attendees.ATTENDEE_RELATIONSHIP, 9795c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Attendees.RELATIONSHIP_ORGANIZER); 9805c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank organizerValues.put(Attendees.ATTENDEE_EMAIL, organizerEmail); 9815c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank entity.addSubValue(Attendees.CONTENT_URI, organizerValues); 9825c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 9835c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Create a message from the Entity we've built. The message will have fields like 9845c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // to, subject, date, and text filled in. There will also be an "inline" attachment 9855c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // which is in iCalendar format 986346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank int flag; 987346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank switch(response) { 988346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank case EmailServiceConstants.MEETING_REQUEST_ACCEPTED: 989346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank flag = Message.FLAG_OUTGOING_MEETING_ACCEPT; 990346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank break; 991346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank case EmailServiceConstants.MEETING_REQUEST_DECLINED: 992346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank flag = Message.FLAG_OUTGOING_MEETING_DECLINE; 993346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank break; 994346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank case EmailServiceConstants.MEETING_REQUEST_TENTATIVE: 995346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank default: 996346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank flag = Message.FLAG_OUTGOING_MEETING_TENTATIVE; 997346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank break; 998346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank } 9995c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank Message outgoingMsg = 1000346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank CalendarUtilities.createMessageForEntity(mContext, entity, flag, 10015c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank meetingInfo.get(MeetingInfo.MEETING_UID), mAccount); 10025c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank // Assuming we got a message back (we might not if the event has been deleted), send it 10035c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (outgoingMsg != null) { 10045c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank EasOutboxService.sendMessage(mContext, mAccount.mId, outgoingMsg); 10055c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 10065c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 10075c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank 10085c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank /** 10097531be7774769c84b499b1de5dc46da3a9468316Marc Blank * Responds to a meeting request. The MeetingResponseRequest is basically our 10107531be7774769c84b499b1de5dc46da3a9468316Marc Blank * wrapper for the meetingResponse service call 10117531be7774769c84b499b1de5dc46da3a9468316Marc Blank * @param req the request (message id and response code) 10127531be7774769c84b499b1de5dc46da3a9468316Marc Blank * @throws IOException 10137531be7774769c84b499b1de5dc46da3a9468316Marc Blank */ 10147531be7774769c84b499b1de5dc46da3a9468316Marc Blank protected void sendMeetingResponse(MeetingResponseRequest req) throws IOException { 10154f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank // Retrieve the message and mailbox; punt if either are null 10167531be7774769c84b499b1de5dc46da3a9468316Marc Blank Message msg = Message.restoreMessageWithId(mContext, req.mMessageId); 10174f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank if (msg == null) return; 10184f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, msg.mMailboxKey); 10194f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank if (mailbox == null) return; 10207531be7774769c84b499b1de5dc46da3a9468316Marc Blank Serializer s = new Serializer(); 10217531be7774769c84b499b1de5dc46da3a9468316Marc Blank s.start(Tags.MREQ_MEETING_RESPONSE).start(Tags.MREQ_REQUEST); 10227531be7774769c84b499b1de5dc46da3a9468316Marc Blank s.data(Tags.MREQ_USER_RESPONSE, Integer.toString(req.mResponse)); 10234f9719f9e1af38a85ec1d2a0df48e0836a2ff94eMarc Blank s.data(Tags.MREQ_COLLECTION_ID, mailbox.mServerId); 10247531be7774769c84b499b1de5dc46da3a9468316Marc Blank s.data(Tags.MREQ_REQ_ID, msg.mServerId); 10257531be7774769c84b499b1de5dc46da3a9468316Marc Blank s.end().end().done(); 10267531be7774769c84b499b1de5dc46da3a9468316Marc Blank HttpResponse res = sendHttpClientPost("MeetingResponse", s.toByteArray()); 10277531be7774769c84b499b1de5dc46da3a9468316Marc Blank int status = res.getStatusLine().getStatusCode(); 10287531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (status == HttpStatus.SC_OK) { 10297531be7774769c84b499b1de5dc46da3a9468316Marc Blank HttpEntity e = res.getEntity(); 10307531be7774769c84b499b1de5dc46da3a9468316Marc Blank int len = (int)e.getContentLength(); 10317531be7774769c84b499b1de5dc46da3a9468316Marc Blank InputStream is = res.getEntity().getContent(); 10327531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (len != 0) { 10337531be7774769c84b499b1de5dc46da3a9468316Marc Blank new MeetingResponseParser(is, this).parse(); 1034346afd9d453de9c15ba92b2af7a0e1b1146a9231Marc Blank sendMeetingResponseMail(msg, req.mResponse); 10357531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 10367531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else if (isAuthError(status)) { 10377531be7774769c84b499b1de5dc46da3a9468316Marc Blank throw new EasAuthenticationException(); 10387531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else { 10397531be7774769c84b499b1de5dc46da3a9468316Marc Blank userLog("Meeting response request failed, code: " + status); 10407531be7774769c84b499b1de5dc46da3a9468316Marc Blank throw new IOException(); 10417531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 10427531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 10437531be7774769c84b499b1de5dc46da3a9468316Marc Blank 1044147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank @SuppressWarnings("deprecation") 10459d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank private String makeUriString(String cmd, String extra) throws IOException { 1046ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Cache the authentication string and the command string 1047ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String safeUserName = URLEncoder.encode(mUserName); 1048ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (mAuthString == null) { 1049ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String cs = mUserName + ':' + mPassword; 1050f78833e76c1decf3a4a1371040a16205d1e59312Doug Zongker mAuthString = "Basic " + Base64.encodeToString(cs.getBytes(), Base64.NO_WRAP); 1051ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mCmdString = "&User=" + safeUserName + "&DeviceId=" + mDeviceId + "&DeviceType=" 1052ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank + mDeviceType; 1053ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1054e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank String us = (mSsl ? (mTrustSsl ? "httpts" : "https") : "http") + "://" + mHostAddress + 1055ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank "/Microsoft-Server-ActiveSync"; 1056ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (cmd != null) { 1057ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank us += "?Cmd=" + cmd + mCmdString; 1058ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1059ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (extra != null) { 1060ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank us += extra; 1061ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1062ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return us; 1063ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1064ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 106520da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 106620da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Set standard HTTP headers, using a policy key if required 106720da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @param method the method we are going to send 106820da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @param usePolicyKey whether or not a policy key should be sent in the headers 106920da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 107020da011530088036d2bf45d3836d6986a4b5d423Marc Blank private void setHeaders(HttpRequestBase method, boolean usePolicyKey) { 10718047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("Authorization", mAuthString); 10728047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("MS-ASProtocolVersion", mProtocolVersion); 10738047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("Connection", "keep-alive"); 10748047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("User-Agent", mDeviceType + '/' + Eas.VERSION); 107520da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (usePolicyKey && (mAccount != null)) { 107620da011530088036d2bf45d3836d6986a4b5d423Marc Blank String key = mAccount.mSecuritySyncKey; 107720da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (key == null || key.length() == 0) { 107820da011530088036d2bf45d3836d6986a4b5d423Marc Blank return; 107920da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 108095fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank if (Eas.PARSER_LOG) { 108195fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank userLog("Policy key: " , key); 108295fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank } 108320da011530088036d2bf45d3836d6986a4b5d423Marc Blank method.setHeader("X-MS-PolicyKey", key); 108420da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 10858047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank } 1086ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1087e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank private ClientConnectionManager getClientConnectionManager() { 1088e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank return SyncManager.getClientConnectionManager(); 1089e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank } 1090e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank 10918047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank private HttpClient getHttpClient(int timeout) { 10928047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpParams params = new BasicHttpParams(); 109316b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank HttpConnectionParams.setConnectionTimeout(params, CONNECTION_TIMEOUT); 10948047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpConnectionParams.setSoTimeout(params, timeout); 1095e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank HttpConnectionParams.setSocketBufferSize(params, 8192); 1096e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank HttpClient client = new DefaultHttpClient(getClientConnectionManager(), params); 1097e44d5875af006f4217718a1c0fc0e235af3863afMarc Blank return client; 10988047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank } 1099ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 11008047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank protected HttpResponse sendHttpClientPost(String cmd, byte[] bytes) throws IOException { 11011b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank return sendHttpClientPost(cmd, new ByteArrayEntity(bytes), COMMAND_TIMEOUT); 11029e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank } 11039e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank 11049e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank protected HttpResponse sendHttpClientPost(String cmd, HttpEntity entity) throws IOException { 11051b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank return sendHttpClientPost(cmd, entity, COMMAND_TIMEOUT); 11061b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 11071b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank 11082033dfc4e2e6e352b34565112266084d72c443f1Marc Blank protected HttpResponse sendPing(byte[] bytes, int heartbeat) throws IOException { 11091b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank Thread.currentThread().setName(mAccount.mDisplayName + ": Ping"); 11101b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (Eas.USER_LOG) { 11112033dfc4e2e6e352b34565112266084d72c443f1Marc Blank userLog("Send ping, timeout: " + heartbeat + "s, high: " + mPingHighWaterMark + 's'); 11121b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 11132033dfc4e2e6e352b34565112266084d72c443f1Marc Blank return sendHttpClientPost(PING_COMMAND, new ByteArrayEntity(bytes), (heartbeat+5)*SECONDS); 11141b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 11151b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank 1116adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank /** 1117adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * Convenience method for executePostWithTimeout for use other than with the Ping command 1118adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank */ 1119adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank protected HttpResponse executePostWithTimeout(HttpClient client, HttpPost method, int timeout) 1120adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank throws IOException { 1121adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank return executePostWithTimeout(client, method, timeout, false); 1122adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1123adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank 1124adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank /** 1125adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * Handle executing an HTTP POST command with proper timeout, watchdog, and ping behavior 1126adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @param client the HttpClient 1127adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @param method the HttpPost 1128adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @param timeout the timeout before failure, in ms 1129adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @param isPingCommand whether the POST is for the Ping command (requires wakelock logic) 1130adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @return the HttpResponse 1131adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank * @throws IOException 1132adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank */ 1133adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank protected HttpResponse executePostWithTimeout(HttpClient client, HttpPost method, int timeout, 1134adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank boolean isPingCommand) throws IOException { 1135adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank synchronized(getSynchronizer()) { 1136adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank mPendingPost = method; 113716b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank long alarmTime = timeout + WATCHDOG_TIMEOUT_ALLOWANCE; 1138adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank if (isPingCommand) { 1139adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank SyncManager.runAsleep(mMailboxId, alarmTime); 1140adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } else { 1141adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank SyncManager.setWatchdogAlarm(mMailboxId, alarmTime); 1142adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1143adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1144adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank try { 1145adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank return client.execute(method); 1146adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } finally { 1147adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank synchronized(getSynchronizer()) { 1148adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank if (isPingCommand) { 1149adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank SyncManager.runAwake(mMailboxId); 1150adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } else { 1151adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank SyncManager.clearWatchdogAlarm(mMailboxId); 1152adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1153adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank mPendingPost = null; 1154adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1155adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1156adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank } 1157adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank 11581b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank protected HttpResponse sendHttpClientPost(String cmd, HttpEntity entity, int timeout) 11591b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank throws IOException { 11601b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank HttpClient client = getHttpClient(timeout); 1161c4bc56c4057d2d7596b75c60ee792676251804f5Marc Blank boolean isPingCommand = cmd.equals(PING_COMMAND); 116285f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank 116385f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank // Split the mail sending commands 116485f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank String extra = null; 116585f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank boolean msg = false; 116685f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank if (cmd.startsWith("SmartForward&") || cmd.startsWith("SmartReply&")) { 11675843b85178a359446f81770ed7734604a1b2fa7dMarc Blank int cmdLength = cmd.indexOf('&'); 116885f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank extra = cmd.substring(cmdLength); 116985f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank cmd = cmd.substring(0, cmdLength); 117085f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank msg = true; 117185f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank } else if (cmd.startsWith("SendMail&")) { 117285f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank msg = true; 117385f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank } 117485f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank 117585f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank String us = makeUriString(cmd, extra); 11768047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpPost method = new HttpPost(URI.create(us)); 117742f47790b84483b15fdf4c7f53b283dd1d56d3faMarc Blank // Send the proper Content-Type header 117842f47790b84483b15fdf4c7f53b283dd1d56d3faMarc Blank // If entity is null (e.g. for attachments), don't set this header 117985f44a524693fb6ede88b3b1e683ca7b493d5983Marc Blank if (msg) { 11808047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("Content-Type", "message/rfc822"); 118142f47790b84483b15fdf4c7f53b283dd1d56d3faMarc Blank } else if (entity != null) { 11828047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank method.setHeader("Content-Type", "application/vnd.ms-sync.wbxml"); 1183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 118420da011530088036d2bf45d3836d6986a4b5d423Marc Blank setHeaders(method, !cmd.equals(PING_COMMAND)); 11859e93e3403ad25add433cb9d2cb0f8cb9154e57eeMarc Blank method.setEntity(entity); 1186adf9fb5f9830ff7933ddc3066cefa5abb52a2773Marc Blank return executePostWithTimeout(client, method, timeout, isPingCommand); 11878047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank } 11888047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank 11898047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank protected HttpResponse sendHttpClientOptions() throws IOException { 11908047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpClient client = getHttpClient(COMMAND_TIMEOUT); 11918047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank String us = makeUriString("OPTIONS", null); 11928047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpOptions method = new HttpOptions(URI.create(us)); 119320da011530088036d2bf45d3836d6986a4b5d423Marc Blank setHeaders(method, false); 11948047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank return client.execute(method); 1195ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1196ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1197ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String getTargetCollectionClassFromCursor(Cursor c) { 1198ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int type = c.getInt(Mailbox.CONTENT_TYPE_COLUMN); 1199ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (type == Mailbox.TYPE_CONTACTS) { 1200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Contacts"; 1201ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else if (type == Mailbox.TYPE_CALENDAR) { 1202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Calendar"; 1203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 1204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Email"; 1205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1206ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1207ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 120820da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 120920da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Negotiate provisioning with the server. First, get policies form the server and see if 121020da011530088036d2bf45d3836d6986a4b5d423Marc Blank * the policies are supported by the device. Then, write the policies to the account and 121120da011530088036d2bf45d3836d6986a4b5d423Marc Blank * tell SecurityPolicy that we have policies in effect. Finally, see if those policies are 121220da011530088036d2bf45d3836d6986a4b5d423Marc Blank * active; if so, acknowledge the policies to the server and get a final policy key that we 121320da011530088036d2bf45d3836d6986a4b5d423Marc Blank * use in future EAS commands and write this key to the account. 121420da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @return whether or not provisioning has been successful 121520da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @throws IOException 121620da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 121720da011530088036d2bf45d3836d6986a4b5d423Marc Blank private boolean tryProvision() throws IOException { 121820da011530088036d2bf45d3836d6986a4b5d423Marc Blank // First, see if provisioning is even possible, i.e. do we support the policies required 121920da011530088036d2bf45d3836d6986a4b5d423Marc Blank // by the server 122020da011530088036d2bf45d3836d6986a4b5d423Marc Blank ProvisionParser pp = canProvision(); 122120da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (pp != null) { 122220da011530088036d2bf45d3836d6986a4b5d423Marc Blank SecurityPolicy sp = SecurityPolicy.getInstance(mContext); 122320da011530088036d2bf45d3836d6986a4b5d423Marc Blank // Get the policies from ProvisionParser 122420da011530088036d2bf45d3836d6986a4b5d423Marc Blank PolicySet ps = pp.getPolicySet(); 122520da011530088036d2bf45d3836d6986a4b5d423Marc Blank // Update the account with a null policyKey (the key we've gotten is 122620da011530088036d2bf45d3836d6986a4b5d423Marc Blank // temporary and cannot be used for syncing) 122720da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (ps.writeAccount(mAccount, null, true, mContext)) { 122820da011530088036d2bf45d3836d6986a4b5d423Marc Blank sp.updatePolicies(mAccount.mId); 122920da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 1230bbc1811956084987767779e31525c5013dab59d8Marc Blank if (pp.getRemoteWipe()) { 12312a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank // We've gotten a remote wipe command 12322b82632e75c2154ab3eda63fbfb00f6236a297ddMarc Blank // If we're not the admin, we can't do the wipe, so just return 12332b82632e75c2154ab3eda63fbfb00f6236a297ddMarc Blank if (!sp.isActiveAdmin()) return false; 1234bbc1811956084987767779e31525c5013dab59d8Marc Blank // First, we've got to acknowledge it, but wrap the wipe in try/catch so that 1235bbc1811956084987767779e31525c5013dab59d8Marc Blank // we wipe the device regardless of any errors in acknowledgment 12362a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank try { 12372a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank acknowledgeRemoteWipe(pp.getPolicyKey()); 12382a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank } catch (Exception e) { 12392a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank // Because remote wipe is such a high priority task, we don't want to 12402a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank // circumvent it if there's an exception in acknowledgment 12412a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank } 12422a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank // Then, tell SecurityPolicy to wipe the device 12432a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank sp.remoteWipe(); 12442a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank return false; 1245bbc1811956084987767779e31525c5013dab59d8Marc Blank } else if (sp.isActive(ps)) { 1246bbc1811956084987767779e31525c5013dab59d8Marc Blank // See if the required policies are in force; if they are, acknowledge the policies 1247bbc1811956084987767779e31525c5013dab59d8Marc Blank // to the server and get the final policy key 1248bbc1811956084987767779e31525c5013dab59d8Marc Blank String policyKey = acknowledgeProvision(pp.getPolicyKey()); 1249bbc1811956084987767779e31525c5013dab59d8Marc Blank if (policyKey != null) { 1250bbc1811956084987767779e31525c5013dab59d8Marc Blank // Write the final policy key to the Account and say we've been successful 1251bbc1811956084987767779e31525c5013dab59d8Marc Blank ps.writeAccount(mAccount, policyKey, true, mContext); 1252bbc1811956084987767779e31525c5013dab59d8Marc Blank return true; 1253bbc1811956084987767779e31525c5013dab59d8Marc Blank } 125420da011530088036d2bf45d3836d6986a4b5d423Marc Blank } else { 125520da011530088036d2bf45d3836d6986a4b5d423Marc Blank // Notify that we are blocked because of policies 125620da011530088036d2bf45d3836d6986a4b5d423Marc Blank sp.policiesRequired(mAccount.mId); 125720da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 125820da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 125920da011530088036d2bf45d3836d6986a4b5d423Marc Blank return false; 126020da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 126120da011530088036d2bf45d3836d6986a4b5d423Marc Blank 1262b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank private String getPolicyType() { 1263d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank return (mProtocolVersionDouble >= 1264d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) ? EAS_12_POLICY_TYPE : EAS_2_POLICY_TYPE; 1265b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank } 1266b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank 126720da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 126820da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Obtain a set of policies from the server and determine whether those policies are supported 126920da011530088036d2bf45d3836d6986a4b5d423Marc Blank * by the device. 127020da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @return the ProvisionParser (holds policies and key) if we receive policies and they are 127120da011530088036d2bf45d3836d6986a4b5d423Marc Blank * supported by the device; null otherwise 127220da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @throws IOException 127320da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 127420da011530088036d2bf45d3836d6986a4b5d423Marc Blank private ProvisionParser canProvision() throws IOException { 12758692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank Serializer s = new Serializer(); 12768692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank s.start(Tags.PROVISION_PROVISION).start(Tags.PROVISION_POLICIES); 1277b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank s.start(Tags.PROVISION_POLICY).data(Tags.PROVISION_POLICY_TYPE, getPolicyType()) 12788692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank .end().end().end().done(); 12798692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank HttpResponse resp = sendHttpClientPost("Provision", s.toByteArray()); 12808692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank int code = resp.getStatusLine().getStatusCode(); 12818692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank if (code == HttpStatus.SC_OK) { 12828692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank InputStream is = resp.getEntity().getContent(); 12838692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank ProvisionParser pp = new ProvisionParser(is, this); 12848692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank if (pp.parse()) { 128520da011530088036d2bf45d3836d6986a4b5d423Marc Blank // If true, we received policies from the server; see if they are supported by 128620da011530088036d2bf45d3836d6986a4b5d423Marc Blank // the framework; if so, return the ProvisionParser containing the policy set and 128720da011530088036d2bf45d3836d6986a4b5d423Marc Blank // temporary key 12888692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank PolicySet ps = pp.getPolicySet(); 12898692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank if (SecurityPolicy.getInstance(mContext).isSupported(ps)) { 129020da011530088036d2bf45d3836d6986a4b5d423Marc Blank return pp; 12918692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 12928692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 12938692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 129420da011530088036d2bf45d3836d6986a4b5d423Marc Blank // On failures, simply return null 129520da011530088036d2bf45d3836d6986a4b5d423Marc Blank return null; 129620da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 129720da011530088036d2bf45d3836d6986a4b5d423Marc Blank 129820da011530088036d2bf45d3836d6986a4b5d423Marc Blank /** 129920da011530088036d2bf45d3836d6986a4b5d423Marc Blank * Acknowledge that we support the policies provided by the server, and that these policies 130020da011530088036d2bf45d3836d6986a4b5d423Marc Blank * are in force. 130120da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @param tempKey the initial (temporary) policy key sent by the server 130220da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @return the final policy key, which can be used for syncing 130320da011530088036d2bf45d3836d6986a4b5d423Marc Blank * @throws IOException 130420da011530088036d2bf45d3836d6986a4b5d423Marc Blank */ 13052a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank private void acknowledgeRemoteWipe(String tempKey) throws IOException { 13062a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank acknowledgeProvisionImpl(tempKey, true); 13072a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank } 13082a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank 130920da011530088036d2bf45d3836d6986a4b5d423Marc Blank private String acknowledgeProvision(String tempKey) throws IOException { 13102a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank return acknowledgeProvisionImpl(tempKey, false); 13112a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank } 13122a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank 13132a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank private String acknowledgeProvisionImpl(String tempKey, boolean remoteWipe) throws IOException { 131420da011530088036d2bf45d3836d6986a4b5d423Marc Blank Serializer s = new Serializer(); 131520da011530088036d2bf45d3836d6986a4b5d423Marc Blank s.start(Tags.PROVISION_PROVISION).start(Tags.PROVISION_POLICIES); 131620da011530088036d2bf45d3836d6986a4b5d423Marc Blank s.start(Tags.PROVISION_POLICY); 1317b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank 1318b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank // Use the proper policy type, depending on EAS version 1319b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank s.data(Tags.PROVISION_POLICY_TYPE, getPolicyType()); 1320b37fc8b9b5f8d1806252051b823bbd1e887f482cMarc Blank 132120da011530088036d2bf45d3836d6986a4b5d423Marc Blank s.data(Tags.PROVISION_POLICY_KEY, tempKey); 132220da011530088036d2bf45d3836d6986a4b5d423Marc Blank s.data(Tags.PROVISION_STATUS, "1"); 13232ca2e1caef16fde39df2c795ac4c9477f9a8c6d9Marc Blank s.end().end(); // PROVISION_POLICY, PROVISION_POLICIES 13242a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank if (remoteWipe) { 13252a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank s.start(Tags.PROVISION_REMOTE_WIPE); 13262a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank s.data(Tags.PROVISION_STATUS, "1"); 13272a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank s.end(); 13282a53f1091aa5a9bb5ca5bc15c730c7e550745ac8Marc Blank } 13292ca2e1caef16fde39df2c795ac4c9477f9a8c6d9Marc Blank s.end().done(); // PROVISION_PROVISION 133020da011530088036d2bf45d3836d6986a4b5d423Marc Blank HttpResponse resp = sendHttpClientPost("Provision", s.toByteArray()); 133120da011530088036d2bf45d3836d6986a4b5d423Marc Blank int code = resp.getStatusLine().getStatusCode(); 133220da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (code == HttpStatus.SC_OK) { 133320da011530088036d2bf45d3836d6986a4b5d423Marc Blank InputStream is = resp.getEntity().getContent(); 133420da011530088036d2bf45d3836d6986a4b5d423Marc Blank ProvisionParser pp = new ProvisionParser(is, this); 133520da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (pp.parse()) { 133620da011530088036d2bf45d3836d6986a4b5d423Marc Blank // Return the final polic key from the ProvisionParser 133720da011530088036d2bf45d3836d6986a4b5d423Marc Blank return pp.getPolicyKey(); 133820da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 133920da011530088036d2bf45d3836d6986a4b5d423Marc Blank } 134020da011530088036d2bf45d3836d6986a4b5d423Marc Blank // On failures, return null 134120da011530088036d2bf45d3836d6986a4b5d423Marc Blank return null; 13428692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank } 13438692940e9f576e7ebbb47f17abd2bc825e42c021Marc Blank 1344ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 1345ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Performs FolderSync 1346ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 1347ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 1348ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws EasParserException 1349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 1350a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank public void runAccountMailbox() throws IOException, EasParserException { 1351fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank // Initialize exit status to success 1352fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank mExitStatus = EmailServiceStatus.SUCCESS; 1353ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1354fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank try { 1355fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank SyncManager.callback() 1356fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank .syncMailboxListStatus(mAccount.mId, EmailServiceStatus.IN_PROGRESS, 0); 1357fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } catch (RemoteException e1) { 1358fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank // Don't care if this fails 1359fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } 1360fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank 1361ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (mAccount.mSyncKey == null) { 1362ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mAccount.mSyncKey = "0"; 13631b275b9408d5b856e2482fa3951827489e9585ccMarc Blank userLog("Account syncKey INIT to 0"); 13649387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler ContentValues cv = new ContentValues(); 13659387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler cv.put(AccountColumns.SYNC_KEY, mAccount.mSyncKey); 13669387711d77c0d3f186f82d9b9512f8a15b4a60dfAndrew Stadler mAccount.update(mContext, cv); 1367ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1368ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 13699d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank boolean firstSync = mAccount.mSyncKey.equals("0"); 13709d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank if (firstSync) { 13711b275b9408d5b856e2482fa3951827489e9585ccMarc Blank userLog("Initial FolderSync"); 13721b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } 13731b275b9408d5b856e2482fa3951827489e9585ccMarc Blank 13744626078bf9d930b2007162db142b5961b38e2166Marc Blank // When we first start up, change all mailboxes to push. 1375ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentValues cv = new ContentValues(); 1376f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH); 1377ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (mContentResolver.update(Mailbox.CONTENT_URI, cv, 13789d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank WHERE_ACCOUNT_AND_SYNC_INTERVAL_PING, 13799d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank new String[] {Long.toString(mAccount.mId)}) > 0) { 13809d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank SyncManager.kick("change ping boxes to push"); 1381ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1382ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1383fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank // Determine our protocol version, if we haven't already and save it in the Account 138485a57898618f48773884e8bc34ca1e8995cc3690Marc Blank // Also re-check protocol version at least once a day (in case of upgrade) 138585a57898618f48773884e8bc34ca1e8995cc3690Marc Blank if (mAccount.mProtocolVersion == null || 138685a57898618f48773884e8bc34ca1e8995cc3690Marc Blank ((System.currentTimeMillis() - mMailbox.mSyncTime) > DAYS)) { 13871b275b9408d5b856e2482fa3951827489e9585ccMarc Blank userLog("Determine EAS protocol version"); 13888047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank HttpResponse resp = sendHttpClientOptions(); 13898047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank int code = resp.getStatusLine().getStatusCode(); 13900a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("OPTIONS response: ", code); 13911b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (code == HttpStatus.SC_OK) { 1392647aa5f1f229947d982548f698c4533fe538f884Marc Blank Header header = resp.getFirstHeader("MS-ASProtocolCommands"); 1393647aa5f1f229947d982548f698c4533fe538f884Marc Blank userLog(header.getValue()); 1394647aa5f1f229947d982548f698c4533fe538f884Marc Blank header = resp.getFirstHeader("ms-asprotocolversions"); 1395d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank try { 1396d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank setupProtocolVersion(this, header); 1397d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank } catch (MessagingException e) { 1398d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // Since we've already validated, this can't really happen 1399d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // But if it does, we'll rethrow this... 1400ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new IOException(); 1401ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1402d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // Save the protocol version 1403d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank cv.clear(); 1404d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // Save the protocol version in the account 1405d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank cv.put(Account.PROTOCOL_VERSION, mProtocolVersion); 1406d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank mAccount.update(mContext, cv); 1407d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank cv.clear(); 1408d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank // Save the sync time of the account mailbox to current time 1409d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis()); 1410d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank mMailbox.update(mContext, cv); 1411d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank } else { 14128047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank errorLog("OPTIONS command failed; throwing IOException"); 14138047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank throw new IOException(); 14140f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } 14150f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } 14161b275b9408d5b856e2482fa3951827489e9585ccMarc Blank 141777424af660458104b732bdcb718874b17d0cab3aMarc Blank // Change all pushable boxes to push when we start the account mailbox 141877424af660458104b732bdcb718874b17d0cab3aMarc Blank if (mAccount.mSyncInterval == Account.CHECK_INTERVAL_PUSH) { 1419fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank cv.clear(); 1420f4ec9557c58b0c5918e3ae4cde23e1355dc0a2afMarc Blank cv.put(Mailbox.SYNC_INTERVAL, Mailbox.CHECK_INTERVAL_PUSH); 142177424af660458104b732bdcb718874b17d0cab3aMarc Blank if (mContentResolver.update(Mailbox.CONTENT_URI, cv, 142277424af660458104b732bdcb718874b17d0cab3aMarc Blank SyncManager.WHERE_IN_ACCOUNT_AND_PUSHABLE, 142377424af660458104b732bdcb718874b17d0cab3aMarc Blank new String[] {Long.toString(mAccount.mId)}) > 0) { 142477424af660458104b732bdcb718874b17d0cab3aMarc Blank userLog("Push account; set pushable boxes to push..."); 142577424af660458104b732bdcb718874b17d0cab3aMarc Blank } 142677424af660458104b732bdcb718874b17d0cab3aMarc Blank } 142777424af660458104b732bdcb718874b17d0cab3aMarc Blank 142877424af660458104b732bdcb718874b17d0cab3aMarc Blank while (!mStop) { 1429afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler userLog("Sending Account syncKey: ", mAccount.mSyncKey); 1430afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler Serializer s = new Serializer(); 1431afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler s.start(Tags.FOLDER_FOLDER_SYNC).start(Tags.FOLDER_SYNC_KEY) 143220da011530088036d2bf45d3836d6986a4b5d423Marc Blank .text(mAccount.mSyncKey).end().end().done(); 1433afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler HttpResponse resp = sendHttpClientPost("FolderSync", s.toByteArray()); 1434afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler if (mStop) break; 1435afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler int code = resp.getStatusLine().getStatusCode(); 1436afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler if (code == HttpStatus.SC_OK) { 1437afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler HttpEntity entity = resp.getEntity(); 1438afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler int len = (int)entity.getContentLength(); 1439afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler if (len != 0) { 1440afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler InputStream is = entity.getContent(); 1441afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler // Returns true if we need to sync again 1442afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler if (new FolderSyncParser(is, new AccountSyncAdapter(mMailbox, this)) 144320da011530088036d2bf45d3836d6986a4b5d423Marc Blank .parse()) { 1444afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler continue; 1445afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler } 1446afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler } 144720da011530088036d2bf45d3836d6986a4b5d423Marc Blank } else if (isProvisionError(code)) { 144820da011530088036d2bf45d3836d6986a4b5d423Marc Blank // If the sync error is a provisioning failure (perhaps the policies changed), 144995fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank // let's try the provisioning procedure 145095fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank // Provisioning must only be attempted for the account mailbox - trying to 145195fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank // provision any other mailbox may result in race conditions and the creation 145295fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank // of multiple policy keys. 145320da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (!tryProvision()) { 145420da011530088036d2bf45d3836d6986a4b5d423Marc Blank // Set the appropriate failure status 145520da011530088036d2bf45d3836d6986a4b5d423Marc Blank mExitStatus = EXIT_SECURITY_FAILURE; 145620da011530088036d2bf45d3836d6986a4b5d423Marc Blank return; 1457094e6da9f5c5728eb10a3572717db2ba55718df3Marc Blank } else { 1458094e6da9f5c5728eb10a3572717db2ba55718df3Marc Blank // If we succeeded, try again... 1459094e6da9f5c5728eb10a3572717db2ba55718df3Marc Blank continue; 1460afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler } 1461afb04a3af2f954affbca09cca462321f46349e00Andrew Stadler } else if (isAuthError(code)) { 14621b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mExitStatus = EXIT_LOGIN_FAILURE; 146320da011530088036d2bf45d3836d6986a4b5d423Marc Blank return; 14640f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } else { 14650a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("FolderSync response error: ", code); 14660f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } 1467ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 14689d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank // Change all push/hold boxes to push 1469fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank cv.clear(); 14709d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank cv.put(Mailbox.SYNC_INTERVAL, Account.CHECK_INTERVAL_PUSH); 14719d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank if (mContentResolver.update(Mailbox.CONTENT_URI, cv, 14729d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank WHERE_PUSH_HOLD_NOT_ACCOUNT_MAILBOX, 14739d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank new String[] {Long.toString(mAccount.mId)}) > 0) { 14749d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank userLog("Set push/hold boxes to push..."); 14759d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank } 14769d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank 14770f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank try { 14780f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank SyncManager.callback() 14799d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank .syncMailboxListStatus(mAccount.mId, mExitStatus, 0); 14800f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } catch (RemoteException e1) { 14810f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank // Don't care if this fails 14820f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } 1483b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank 1484b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank // Before each run of the pingLoop, if this Account has a PolicySet, make sure it's 1485b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank // active; otherwise, clear out the key/flag. This should cause a provisioning 1486b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank // error on the next POST, and start the security sequence over again 1487b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank String key = mAccount.mSecuritySyncKey; 1488b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank if (!TextUtils.isEmpty(key)) { 1489b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank PolicySet ps = new PolicySet(mAccount); 1490b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank SecurityPolicy sp = SecurityPolicy.getInstance(mContext); 1491b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank if (!sp.isActive(ps)) { 1492b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank cv.clear(); 1493b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank cv.put(AccountColumns.SECURITY_FLAGS, 0); 1494b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank cv.putNull(AccountColumns.SECURITY_SYNC_KEY); 1495b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank long accountId = mAccount.mId; 1496b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank mContentResolver.update(ContentUris.withAppendedId( 1497b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank Account.CONTENT_URI, accountId), cv, null, null); 1498b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank sp.policiesRequired(accountId); 1499b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank } 1500b25739e9cf48fb450a6906d8f82ddd58a8ade40cMarc Blank } 1501fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank 15020f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank // Wait for push notifications. 15030f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank String threadName = Thread.currentThread().getName(); 15040f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank try { 15050f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank runPingLoop(); 15060f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } catch (StaleFolderListException e) { 15070f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank // We break out if we get told about a stale folder list 15080f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank userLog("Ping interrupted; folder list requires sync..."); 15090f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } finally { 15100f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank Thread.currentThread().setName(threadName); 15110f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } 1512ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 15130f2a0c93a85ce615ffe521bbc6f5217afae7094bMarc Blank } catch (IOException e) { 1514fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank // We catch this here to send the folder sync status callback 1515fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank // A folder sync failed callback will get sent from run() 1516fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank try { 15174d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank if (!mStop) { 15184d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank SyncManager.callback() 15194d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank .syncMailboxListStatus(mAccount.mId, 15204d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank EmailServiceStatus.CONNECTION_ERROR, 0); 15214d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank } 1522fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } catch (RemoteException e1) { 1523fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank // Don't care if this fails 1524fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } 152596293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank throw e; 1526ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1527ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 15294ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank private void pushFallback(long mailboxId) { 1530c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, mailboxId); 15315ea28d42b2aee64747a35d8bf012afff1604bc4cMakoto Onuki if (mailbox == null) { 15325ea28d42b2aee64747a35d8bf012afff1604bc4cMakoto Onuki return; 15335ea28d42b2aee64747a35d8bf012afff1604bc4cMakoto Onuki } 1534c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank ContentValues cv = new ContentValues(); 15354626078bf9d930b2007162db142b5961b38e2166Marc Blank int mins = PING_FALLBACK_PIM; 1536c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank if (mailbox.mType == Mailbox.TYPE_INBOX) { 15374626078bf9d930b2007162db142b5961b38e2166Marc Blank mins = PING_FALLBACK_INBOX; 1538c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 15394626078bf9d930b2007162db142b5961b38e2166Marc Blank cv.put(Mailbox.SYNC_INTERVAL, mins); 15404626078bf9d930b2007162db142b5961b38e2166Marc Blank mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId), 15414626078bf9d930b2007162db142b5961b38e2166Marc Blank cv, null, null); 154227cf341571fac3d8dbe866f503c34fc31e02bf85Marc Blank errorLog("*** PING ERROR LOOP: Set " + mailbox.mDisplayName + " to " + mins + " min sync"); 15434626078bf9d930b2007162db142b5961b38e2166Marc Blank SyncManager.kick("push fallback"); 1544368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1545368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank 15464ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank /** 15474ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank * Simplistic attempt to determine a NAT timeout, based on experience with various carriers 15482675dc0ccc3bebaae73900c3446b2c6c08e40eefMarc Blank * and networks. The string "reset by peer" is very common in these situations, so we look for 15492675dc0ccc3bebaae73900c3446b2c6c08e40eefMarc Blank * that specifically. We may add additional tests here as more is learned. 15504ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank * @param message 15514ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank * @return whether this message is likely associated with a NAT failure 15524ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank */ 15534ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank private boolean isLikelyNatFailure(String message) { 15544ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank if (message == null) return false; 15552675dc0ccc3bebaae73900c3446b2c6c08e40eefMarc Blank if (message.contains("reset by peer")) { 15564ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank return true; 15574ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank } 15584ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank return false; 15594ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank } 15604ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank 15614ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank private void runPingLoop() throws IOException, StaleFolderListException { 15621b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank int pingHeartbeat = mPingHeartbeat; 156374c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank userLog("runPingLoop"); 1564ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Do push for all sync services here 1565c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank long endTime = System.currentTimeMillis() + (30*MINUTES); 15661b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank HashMap<String, Integer> pingErrorMap = new HashMap<String, Integer>(); 15672033dfc4e2e6e352b34565112266084d72c443f1Marc Blank ArrayList<String> readyMailboxes = new ArrayList<String>(); 15682033dfc4e2e6e352b34565112266084d72c443f1Marc Blank ArrayList<String> notReadyMailboxes = new ArrayList<String>(); 15697672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank int pingWaitCount = 0; 1570f78833e76c1decf3a4a1371040a16205d1e59312Doug Zongker 15717ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank while ((System.currentTimeMillis() < endTime) && !mStop) { 1572ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Count of pushable mailboxes 1573ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int pushCount = 0; 1574ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Count of mailboxes that can be pushed right now 1575ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int canPushCount = 0; 15768a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank // Count of uninitialized boxes 15778a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank int uninitCount = 0; 1578f78833e76c1decf3a4a1371040a16205d1e59312Doug Zongker 15797c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank Serializer s = new Serializer(); 1580ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = mContentResolver.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION, 158122bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank MailboxColumns.ACCOUNT_KEY + '=' + mAccount.mId + 158222bc4e0e4f4a5e43e4eea8d59e1961860c507594Marc Blank AND_FREQUENCY_PING_PUSH_AND_NOT_ACCOUNT_MAILBOX, null, null); 15832033dfc4e2e6e352b34565112266084d72c443f1Marc Blank notReadyMailboxes.clear(); 15842033dfc4e2e6e352b34565112266084d72c443f1Marc Blank readyMailboxes.clear(); 1585ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1586ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Loop through our pushed boxes seeing what is available to push 1587ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 1588ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank pushCount++; 1589ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Two requirements for push: 1590ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // 1) SyncManager tells us the mailbox is syncable (not running, not stopped) 1591ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // 2) The syncKey isn't "0" (i.e. it's synced at least once) 1592ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank long mailboxId = c.getLong(Mailbox.CONTENT_ID_COLUMN); 159377424af660458104b732bdcb718874b17d0cab3aMarc Blank int pingStatus = SyncManager.pingStatus(mailboxId); 159477424af660458104b732bdcb718874b17d0cab3aMarc Blank String mailboxName = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN); 159577424af660458104b732bdcb718874b17d0cab3aMarc Blank if (pingStatus == SyncManager.PING_STATUS_OK) { 1596ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String syncKey = c.getString(Mailbox.CONTENT_SYNC_KEY_COLUMN); 15977ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank if ((syncKey == null) || syncKey.equals("0")) { 1598a05c26d8d2cce3faa152096cb8116fce375c6d81Marc Blank // We can't push until the initial sync is done 159977424af660458104b732bdcb718874b17d0cab3aMarc Blank pushCount--; 16008a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank uninitCount++; 1601ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 1602ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1603ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank 1604ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (canPushCount++ == 0) { 1605ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Initialize the Ping command 160696293e01d2c94b7a811f06f56e5f115dd48bc03eMarc Blank s.start(Tags.PING_PING) 160705381a6662f28609e8005023515abb82af00e1d4Marc Blank .data(Tags.PING_HEARTBEAT_INTERVAL, 16081b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank Integer.toString(pingHeartbeat)) 16097c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .start(Tags.PING_FOLDERS); 1610ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 16111b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank 1612ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String folderClass = getTargetCollectionClassFromCursor(c); 16137c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.PING_FOLDER) 16147c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.PING_ID, c.getString(Mailbox.CONTENT_SERVER_ID_COLUMN)) 16157c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.PING_CLASS, folderClass) 16167c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .end(); 16172033dfc4e2e6e352b34565112266084d72c443f1Marc Blank readyMailboxes.add(mailboxName); 16187ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank } else if ((pingStatus == SyncManager.PING_STATUS_RUNNING) || 16197ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank (pingStatus == SyncManager.PING_STATUS_WAITING)) { 16202033dfc4e2e6e352b34565112266084d72c443f1Marc Blank notReadyMailboxes.add(mailboxName); 162177424af660458104b732bdcb718874b17d0cab3aMarc Blank } else if (pingStatus == SyncManager.PING_STATUS_UNABLE) { 162277424af660458104b732bdcb718874b17d0cab3aMarc Blank pushCount--; 16230a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog(mailboxName, " in error state; ignore"); 162477424af660458104b732bdcb718874b17d0cab3aMarc Blank continue; 1625ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1626ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1627ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1628ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1629ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1630ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 16312033dfc4e2e6e352b34565112266084d72c443f1Marc Blank if (Eas.USER_LOG) { 16322033dfc4e2e6e352b34565112266084d72c443f1Marc Blank if (!notReadyMailboxes.isEmpty()) { 16332033dfc4e2e6e352b34565112266084d72c443f1Marc Blank userLog("Ping not ready for: " + notReadyMailboxes); 16342033dfc4e2e6e352b34565112266084d72c443f1Marc Blank } 16352033dfc4e2e6e352b34565112266084d72c443f1Marc Blank if (!readyMailboxes.isEmpty()) { 16362033dfc4e2e6e352b34565112266084d72c443f1Marc Blank userLog("Ping ready for: " + readyMailboxes); 16372033dfc4e2e6e352b34565112266084d72c443f1Marc Blank } 16382033dfc4e2e6e352b34565112266084d72c443f1Marc Blank } 1639f78833e76c1decf3a4a1371040a16205d1e59312Doug Zongker 16407672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank // If we've waited 10 seconds or more, just ping with whatever boxes are ready 16417672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank // But use a shorter than normal heartbeat 16422033dfc4e2e6e352b34565112266084d72c443f1Marc Blank boolean forcePing = !notReadyMailboxes.isEmpty() && (pingWaitCount > 5); 16437672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank 16447672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank if ((canPushCount > 0) && ((canPushCount == pushCount) || forcePing)) { 16457672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank // If all pingable boxes are ready for push, send Ping to the server 16467c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.end().end().done(); 16477672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank pingWaitCount = 0; 1648e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank mPostReset = false; 1649e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank mPostAborted = false; 16507c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank 16511b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // If we've been stopped, this is a good time to return 16524d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank if (mStop) return; 1653368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank 165474c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank long pingTime = SystemClock.elapsedRealtime(); 16551b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank try { 16561b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // Send the ping, wrapped by appropriate timeout/alarm 16577672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank if (forcePing) { 16587672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank userLog("Forcing ping after waiting for all boxes to be ready"); 16597672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank } 16607672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank HttpResponse res = 16617672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank sendPing(s.toByteArray(), forcePing ? PING_FORCE_HEARTBEAT : pingHeartbeat); 1662c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank 16631b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank int code = res.getStatusLine().getStatusCode(); 16641b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank userLog("Ping response: ", code); 1665368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank 16661b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // Return immediately if we've been asked to stop during the ping 16671b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (mStop) { 16681b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank userLog("Stopping pingLoop"); 16691b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank return; 16701b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 16711b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank 16721b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (code == HttpStatus.SC_OK) { 1673b1ffc81c7e3ee9297133a7924092192998ab3839Marc Blank // Make sure to clear out any pending sync errors 1674b1ffc81c7e3ee9297133a7924092192998ab3839Marc Blank SyncManager.removeFromSyncErrorMap(mMailboxId); 16751b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank HttpEntity e = res.getEntity(); 16761b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank int len = (int)e.getContentLength(); 16771b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank InputStream is = res.getEntity().getContent(); 167895e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank if (len != 0) { 16798d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank int pingResult = parsePingResult(is, mContentResolver, pingErrorMap); 16807672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank // If our ping completed (status = 1), and we weren't forced and we're 16817672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank // not at the maximum, try increasing timeout by two minutes 16828d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank if (pingResult == PROTOCOL_PING_STATUS_COMPLETED && !forcePing) { 16837672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank if (pingHeartbeat > mPingHighWaterMark) { 16847672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank mPingHighWaterMark = pingHeartbeat; 16857672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank userLog("Setting high water mark at: ", mPingHighWaterMark); 16867672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank } 16877672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank if ((pingHeartbeat < PING_MAX_HEARTBEAT) && 16887672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank !mPingHeartbeatDropped) { 16897672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank pingHeartbeat += PING_HEARTBEAT_INCREMENT; 16907672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank if (pingHeartbeat > PING_MAX_HEARTBEAT) { 16917672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank pingHeartbeat = PING_MAX_HEARTBEAT; 16927672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank } 16937672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank userLog("Increasing ping heartbeat to ", pingHeartbeat, "s"); 16941b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 16951b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 16961b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } else { 16971b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank userLog("Ping returned empty result; throwing IOException"); 16981b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank throw new IOException(); 16991b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 17001b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } else if (isAuthError(code)) { 17011b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mExitStatus = EXIT_LOGIN_FAILURE; 17021b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank userLog("Authorization error during Ping: ", code); 1703ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new IOException(); 1704ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 17051b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } catch (IOException e) { 1706252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank String message = e.getMessage(); 17071b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // If we get the exception that is indicative of a NAT timeout and if we 17081b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // haven't yet "fixed" the timeout, back off by two minutes and "fix" it 1709252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank boolean hasMessage = message != null; 1710252e460a92f91d9549a3b41376410f7ac7263db8Marc Blank userLog("IOException runPingLoop: " + (hasMessage ? message : "[no message]")); 1711e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank if (mPostReset) { 1712e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // Nothing to do in this case; this is SyncManager telling us to try another 1713e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // ping. 17144ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank } else if (mPostAborted || isLikelyNatFailure(message)) { 1715b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank long pingLength = SystemClock.elapsedRealtime() - pingTime; 17167ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank if ((pingHeartbeat > PING_MIN_HEARTBEAT) && 17177ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank (pingHeartbeat > mPingHighWaterMark)) { 17181b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank pingHeartbeat -= PING_HEARTBEAT_INCREMENT; 17191b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mPingHeartbeatDropped = true; 17201b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (pingHeartbeat < PING_MIN_HEARTBEAT) { 17211b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank pingHeartbeat = PING_MIN_HEARTBEAT; 17221b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 17231b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank userLog("Decreased ping heartbeat to ", pingHeartbeat, "s"); 1724e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } else if (mPostAborted) { 1725e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // There's no point in throwing here; this can happen in two cases 1726e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // 1) An alarm, which indicates minutes without activity; no sense 1727e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // backing off 1728e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // 2) SyncManager abort, due to sync of mailbox. Again, we want to 1729e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank // keep on trying to ping 1730e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank userLog("Ping aborted; retry"); 1731e8ea6833e0541f0a0a3ceb1d78c84ac9ce359210Marc Blank } else if (pingLength < 2000) { 1732b9781ea88a58e746b74076ec499e9885e195acc9Marc Blank userLog("Abort or NAT type return < 2 seconds; throwing IOException"); 173374c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank throw e; 173474c196e6645cd5547c3ff2e7b6be377c00f1ca74Marc Blank } else { 17354ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank userLog("NAT type IOException"); 17361b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 17372675dc0ccc3bebaae73900c3446b2c6c08e40eefMarc Blank } else if (hasMessage && message.contains("roken pipe")) { 17382675dc0ccc3bebaae73900c3446b2c6c08e40eefMarc Blank // The "broken pipe" error (uppercase or lowercase "b") seems to be an 17392675dc0ccc3bebaae73900c3446b2c6c08e40eefMarc Blank // internal error, so let's not throw an exception (which leads to delays) 17402675dc0ccc3bebaae73900c3446b2c6c08e40eefMarc Blank // but rather simply run through the loop again 17411b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } else { 17421b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank throw e; 17431b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 1744ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 17452033dfc4e2e6e352b34565112266084d72c443f1Marc Blank } else if (forcePing) { 17462033dfc4e2e6e352b34565112266084d72c443f1Marc Blank // In this case, there aren't any boxes that are pingable, but there are boxes 17472033dfc4e2e6e352b34565112266084d72c443f1Marc Blank // waiting (for IOExceptions) 17482033dfc4e2e6e352b34565112266084d72c443f1Marc Blank userLog("pingLoop waiting 60s for any pingable boxes"); 17492033dfc4e2e6e352b34565112266084d72c443f1Marc Blank sleep(60*SECONDS, true); 1750ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else if (pushCount > 0) { 17511ec0390ce7c27890a46f877d858f7ac6b6a8920cMarc Blank // If we want to Ping, but can't just yet, wait a little bit 17521ec0390ce7c27890a46f877d858f7ac6b6a8920cMarc Blank // TODO Change sleep to wait and use notify from SyncManager when a sync ends 17532033dfc4e2e6e352b34565112266084d72c443f1Marc Blank sleep(2*SECONDS, false); 17547672d9eac8a70bae0a4310b7ae83f861992de853Marc Blank pingWaitCount++; 17552033dfc4e2e6e352b34565112266084d72c443f1Marc Blank //userLog("pingLoop waited 2s for: ", (pushCount - canPushCount), " box(es)"); 17568a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank } else if (uninitCount > 0) { 17578a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank // In this case, we're doing an initial sync of at least one mailbox. Since this 17588a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank // is typically a one-time case, I'm ok with trying again every 10 seconds until 17598a1fe23c8b305f3f401d3155ceebbb9a975cb7c2Marc Blank // we're in one of the other possible states. 17602033dfc4e2e6e352b34565112266084d72c443f1Marc Blank userLog("pingLoop waiting for initial sync of ", uninitCount, " box(es)"); 17612033dfc4e2e6e352b34565112266084d72c443f1Marc Blank sleep(10*SECONDS, true); 1762ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 1763c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank // We've got nothing to do, so we'll check again in 30 minutes at which time 17641b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // we'll update the folder list. Let the device sleep in the meantime... 17652033dfc4e2e6e352b34565112266084d72c443f1Marc Blank userLog("pingLoop sleeping for 30m"); 17662033dfc4e2e6e352b34565112266084d72c443f1Marc Blank sleep(30*MINUTES, true); 1767ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1768ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 17692ff60f148dfc0ea1b35168a01d8c48de5658e073Marc Blank 17702ff60f148dfc0ea1b35168a01d8c48de5658e073Marc Blank // Save away the current heartbeat 17712ff60f148dfc0ea1b35168a01d8c48de5658e073Marc Blank mPingHeartbeat = pingHeartbeat; 1772ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1773ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 17744ea0f8f6310f68c327ca8f219301eecabedde27aMarc Blank private void sleep(long ms, boolean runAsleep) { 17752033dfc4e2e6e352b34565112266084d72c443f1Marc Blank if (runAsleep) { 17762033dfc4e2e6e352b34565112266084d72c443f1Marc Blank SyncManager.runAsleep(mMailboxId, ms+(5*SECONDS)); 17772033dfc4e2e6e352b34565112266084d72c443f1Marc Blank } 1778ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1779ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Thread.sleep(ms); 1780ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (InterruptedException e) { 1781ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Doesn't matter whether we stop early; it's the thought that counts 17822033dfc4e2e6e352b34565112266084d72c443f1Marc Blank } finally { 17832033dfc4e2e6e352b34565112266084d72c443f1Marc Blank if (runAsleep) { 17842033dfc4e2e6e352b34565112266084d72c443f1Marc Blank SyncManager.runAwake(mMailboxId); 17852033dfc4e2e6e352b34565112266084d72c443f1Marc Blank } 1786ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1787ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1788ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 17898d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank private int parsePingResult(InputStream is, ContentResolver cr, 17908d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank HashMap<String, Integer> errorMap) 1791ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throws IOException, StaleFolderListException { 17928047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank PingParser pp = new PingParser(is, this); 1793ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (pp.parse()) { 1794ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // True indicates some mailboxes need syncing... 1795ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // syncList has the serverId's of the mailboxes... 1796ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mBindArguments[0] = Long.toString(mAccount.mId); 17971b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mPingChangeList = pp.getSyncList(); 17981b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank for (String serverId: mPingChangeList) { 1799d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank mBindArguments[1] = serverId; 1800ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = cr.query(Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION, 1801ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank WHERE_ACCOUNT_KEY_AND_SERVER_ID, mBindArguments, null); 1802ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1803ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 18048d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank 18058d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank /** 18068d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * Check the boxes reporting changes to see if there really were any... 18078d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * We do this because bugs in various Exchange servers can put us into a 18088d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * looping behavior by continually reporting changes in a mailbox, even when 18098d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * there aren't any. 18108d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * 18118d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * This behavior is seemingly random, and therefore we must code defensively 18128d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * by backing off of push behavior when it is detected. 18138d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * 18148d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * One known cause, on certain Exchange 2003 servers, is acknowledged by 18158d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * Microsoft, and the server hotfix for this case can be found at 18168d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank * http://support.microsoft.com/kb/923282 18178d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank */ 18188d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank 18198d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // Check the status of the last sync 18208d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank String status = c.getString(Mailbox.CONTENT_SYNC_STATUS_COLUMN); 18218d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank int type = SyncManager.getStatusType(status); 18228d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // This check should always be true... 18238d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank if (type == SyncManager.SYNC_PING) { 18248d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank int changeCount = SyncManager.getStatusChangeCount(status); 18258d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank if (changeCount > 0) { 18268d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank errorMap.remove(serverId); 18278d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank } else if (changeCount == 0) { 18288d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // This means that a ping reported changes in error; we keep a count 18298d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // of consecutive errors of this kind 18308d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank String name = c.getString(Mailbox.CONTENT_DISPLAY_NAME_COLUMN); 18318d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank Integer failures = errorMap.get(serverId); 18328d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank if (failures == null) { 18338d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank userLog("Last ping reported changes in error for: ", name); 18348d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank errorMap.put(serverId, 1); 18358d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank } else if (failures > MAX_PING_FAILURES) { 18368d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // We'll back off of push for this box 18378d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank pushFallback(c.getLong(Mailbox.CONTENT_ID_COLUMN)); 18388d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank continue; 18398d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank } else { 18408d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank userLog("Last ping reported changes in error for: ", name); 18418d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank errorMap.put(serverId, failures + 1); 18428d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank } 18438d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank } 18448d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank } 18458d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank 18468d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // If there were no problems with previous sync, we'll start another one 18474d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank SyncManager.startManualSync(c.getLong(Mailbox.CONTENT_ID_COLUMN), 1848ed5b71376cb6fc3f54d63268afbd798e0b0c0a1bMarc Blank SyncManager.SYNC_PING, null); 1849ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1850ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1851ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1852ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1853ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1854ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 18551b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank return pp.getSyncStatus(); 1856ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1857ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 18585862a85e17e81866ca82a9905577931947fbd44eMarc Blank private String getEmailFilter() { 1859368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank String filter = Eas.FILTER_1_WEEK; 1860368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank switch (mAccount.mSyncLookback) { 1861368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank case com.android.email.Account.SYNC_WINDOW_1_DAY: { 1862368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank filter = Eas.FILTER_1_DAY; 1863368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank break; 1864368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1865368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank case com.android.email.Account.SYNC_WINDOW_3_DAYS: { 1866368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank filter = Eas.FILTER_3_DAYS; 1867368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank break; 1868368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1869368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank case com.android.email.Account.SYNC_WINDOW_1_WEEK: { 1870368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank filter = Eas.FILTER_1_WEEK; 1871368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank break; 1872368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1873368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank case com.android.email.Account.SYNC_WINDOW_2_WEEKS: { 1874368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank filter = Eas.FILTER_2_WEEKS; 1875368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank break; 1876368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1877368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank case com.android.email.Account.SYNC_WINDOW_1_MONTH: { 1878368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank filter = Eas.FILTER_1_MONTH; 1879368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank break; 1880368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1881368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank case com.android.email.Account.SYNC_WINDOW_ALL: { 1882368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank filter = Eas.FILTER_ALL; 1883368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank break; 1884368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1885368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1886368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank return filter; 1887368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank } 1888368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank 1889ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 1890ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Common code to sync E+PIM data 1891ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 1892ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @param target, an EasMailbox, EasContacts, or EasCalendar object 1893ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 18947c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank public void sync(AbstractSyncAdapter target) throws IOException { 1895ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Mailbox mailbox = target.mMailbox; 1896ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1897ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank boolean moreAvailable = true; 1898ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (!mStop && moreAvailable) { 18991b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // If we have no connectivity, just exit cleanly. SyncManager will start us up again 19001b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank // when connectivity has returned 19011b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (!hasConnectivity()) { 19021b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank userLog("No connectivity in sync; finishing sync"); 19031b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mExitStatus = EXIT_DONE; 19041b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank return; 1905aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank } 1906aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank 1907aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank // Every time through the loop we check to see if we're still syncable 1908aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank if (!target.isSyncable()) { 1909aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank mExitStatus = EXIT_DONE; 1910aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank return; 19111b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } 1912ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 19137531be7774769c84b499b1de5dc46da3a9468316Marc Blank // Now, handle various requests 1914d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank while (true) { 19157531be7774769c84b499b1de5dc46da3a9468316Marc Blank Request req = null; 19167531be7774769c84b499b1de5dc46da3a9468316Marc Blank synchronized (mRequests) { 19177531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (mRequests.isEmpty()) { 1918d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank break; 1919d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } else { 19207531be7774769c84b499b1de5dc46da3a9468316Marc Blank req = mRequests.get(0); 1921d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 1922d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 19237531be7774769c84b499b1de5dc46da3a9468316Marc Blank 19247531be7774769c84b499b1de5dc46da3a9468316Marc Blank // Our two request types are PartRequest (loading attachment) and 19257531be7774769c84b499b1de5dc46da3a9468316Marc Blank // MeetingResponseRequest (respond to a meeting request) 19267531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (req instanceof PartRequest) { 19277531be7774769c84b499b1de5dc46da3a9468316Marc Blank getAttachment((PartRequest)req); 19287531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else if (req instanceof MeetingResponseRequest) { 19297531be7774769c84b499b1de5dc46da3a9468316Marc Blank sendMeetingResponse((MeetingResponseRequest)req); 19307531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 19317531be7774769c84b499b1de5dc46da3a9468316Marc Blank 19327531be7774769c84b499b1de5dc46da3a9468316Marc Blank // If there's an exception handling the request, we'll throw it 19337531be7774769c84b499b1de5dc46da3a9468316Marc Blank // Otherwise, we remove the request 19347531be7774769c84b499b1de5dc46da3a9468316Marc Blank synchronized(mRequests) { 19357531be7774769c84b499b1de5dc46da3a9468316Marc Blank mRequests.remove(req); 1936d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 1937d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank } 1938d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank 19397c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank Serializer s = new Serializer(); 1940c5f958fb9eeafb267f23f2e25769d345be5b22adMarc Blank 1941ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String className = target.getCollectionName(); 1942c5f958fb9eeafb267f23f2e25769d345be5b22adMarc Blank 1943c5f958fb9eeafb267f23f2e25769d345be5b22adMarc Blank // STOPSHIP Remove the following if statement; temporary logging for Calendar sync 19449972a855025df83129b9c7de4010024188219bd3Marc Blank if (className.equals("Calendar") && Eas.PARSER_LOG) { 1945c5f958fb9eeafb267f23f2e25769d345be5b22adMarc Blank s = new Serializer(true, true); 1946c5f958fb9eeafb267f23f2e25769d345be5b22adMarc Blank } 1947c5f958fb9eeafb267f23f2e25769d345be5b22adMarc Blank 194848af7392c82262d17700e3fbdccf3a582809d449Marc Blank String syncKey = target.getSyncKey(); 19498d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank userLog("sync, sending ", className, " syncKey: ", syncKey); 19507c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_SYNC) 19517c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .start(Tags.SYNC_COLLECTIONS) 19527c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .start(Tags.SYNC_COLLECTION) 19537c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.SYNC_CLASS, className) 195448af7392c82262d17700e3fbdccf3a582809d449Marc Blank .data(Tags.SYNC_SYNC_KEY, syncKey) 19557c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.SYNC_COLLECTION_ID, mailbox.mServerId) 19567c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .tag(Tags.SYNC_DELETES_AS_MOVES); 1957ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 195816b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // Start with the default timeout 195916b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank int timeout = COMMAND_TIMEOUT; 196048af7392c82262d17700e3fbdccf3a582809d449Marc Blank if (!syncKey.equals("0")) { 196116b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // EAS doesn't like GetChanges if the syncKey is "0"; not documented 19627c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.tag(Tags.SYNC_GET_CHANGES); 196316b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank } else { 19649e029e58959e581637a1288a1ff9ef44cad63576Marc Blank // Use enormous timeout for initial sync, which empirically can take a while longer 19659e029e58959e581637a1288a1ff9ef44cad63576Marc Blank timeout = 120*SECONDS; 1966ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 19671b275b9408d5b856e2482fa3951827489e9585ccMarc Blank s.data(Tags.SYNC_WINDOW_SIZE, 19681b275b9408d5b856e2482fa3951827489e9585ccMarc Blank className.equals("Email") ? EMAIL_WINDOW_SIZE : PIM_WINDOW_SIZE); 1969895d1e3132622653160516d420231ed366ab411bMarc Blank 1970895d1e3132622653160516d420231ed366ab411bMarc Blank // Handle options 1971895d1e3132622653160516d420231ed366ab411bMarc Blank s.start(Tags.SYNC_OPTIONS); 1972895d1e3132622653160516d420231ed366ab411bMarc Blank // Set the lookback appropriately (EAS calls this a "filter") for all but Contacts 19735862a85e17e81866ca82a9905577931947fbd44eMarc Blank if (className.equals("Email")) { 19745862a85e17e81866ca82a9905577931947fbd44eMarc Blank s.data(Tags.SYNC_FILTER_TYPE, getEmailFilter()); 19755862a85e17e81866ca82a9905577931947fbd44eMarc Blank } else if (className.equals("Calendar")) { 197616b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank // TODO Force two weeks for calendar until we can set this! 197716b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank s.data(Tags.SYNC_FILTER_TYPE, Eas.FILTER_2_WEEKS); 1978ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1979895d1e3132622653160516d420231ed366ab411bMarc Blank // Set the truncation amount for all classes 1980d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 19817c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.BASE_BODY_PREFERENCE) 1982368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank // HTML for email; plain text for everything else 1983c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank .data(Tags.BASE_TYPE, (className.equals("Email") ? Eas.BODY_PREFERENCE_HTML 1984895d1e3132622653160516d420231ed366ab411bMarc Blank : Eas.BODY_PREFERENCE_TEXT)) 1985895d1e3132622653160516d420231ed366ab411bMarc Blank .data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE) 19867c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .end(); 1987895d1e3132622653160516d420231ed366ab411bMarc Blank } else { 1988895d1e3132622653160516d420231ed366ab411bMarc Blank s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 1989ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1990895d1e3132622653160516d420231ed366ab411bMarc Blank s.end(); 1991ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1992ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Send our changes up to the server 199348af7392c82262d17700e3fbdccf3a582809d449Marc Blank target.sendLocalChanges(s); 1994ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 19957c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.end().end().end().done(); 199616b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank HttpResponse resp = sendHttpClientPost("Sync", new ByteArrayEntity(s.toByteArray()), 199716b445cd6c4de57ae144fe76449ac6953333f0e9Marc Blank timeout); 19988047ef058e41c164c2c8ab230ae8d123f042c167Marc Blank int code = resp.getStatusLine().getStatusCode(); 19991b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank if (code == HttpStatus.SC_OK) { 200095e8381b688152dd60e0eda84aef3bfc14fd1672Marc Blank InputStream is = resp.getEntity().getContent(); 2001ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (is != null) { 200248af7392c82262d17700e3fbdccf3a582809d449Marc Blank moreAvailable = target.parse(is); 200348af7392c82262d17700e3fbdccf3a582809d449Marc Blank target.cleanup(); 20048d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank } else { 20058d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank userLog("Empty input stream in sync command response"); 2006ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 2007ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 20080a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Sync response error: ", code); 200920da011530088036d2bf45d3836d6986a4b5d423Marc Blank if (isProvisionError(code)) { 201095fcf9c6db1886fdd0d3f98259671cbe3f7ec0d5Marc Blank mExitStatus = EXIT_SECURITY_FAILURE; 201120da011530088036d2bf45d3836d6986a4b5d423Marc Blank } else if (isAuthError(code)) { 20121b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mExitStatus = EXIT_LOGIN_FAILURE; 20131b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank } else { 20141b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mExitStatus = EXIT_IO_ERROR; 2015ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 2016ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return; 2017ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 2018ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 20191b06024587a4499bcf3f9005337e8f7cae5ffa26Marc Blank mExitStatus = EXIT_DONE; 2020ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 2021ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 202222e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank protected boolean setupService() { 20237ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank // Make sure account and mailbox are always the latest from the database 20247ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank mAccount = Account.restoreAccountWithId(mContext, mAccount.mId); 202522e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank if (mAccount == null) return false; 20267ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank mMailbox = Mailbox.restoreMailboxWithId(mContext, mMailbox.mId); 202722e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank if (mMailbox == null) return false; 2028ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mThread = Thread.currentThread(); 20297310cbacf2cf614c949330faff3882082054c120Marc Blank android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); 2030ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank TAG = mThread.getName(); 20319d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank 2032ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank HostAuth ha = HostAuth.restoreHostAuthWithId(mContext, mAccount.mHostAuthKeyRecv); 203322e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank if (ha == null) return false; 2034ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mHostAddress = ha.mAddress; 2035ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUserName = ha.mLogin; 2036ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mPassword = ha.mPassword; 20373852792f1c79dcfca6dc9be67f11e4a53788a462Marc Blank 2038fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank // Set up our protocol version from the Account 20393852792f1c79dcfca6dc9be67f11e4a53788a462Marc Blank mProtocolVersion = mAccount.mProtocolVersion; 2040fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank // If it hasn't been set up, start with default version 2041fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank if (mProtocolVersion == null) { 2042d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank mProtocolVersion = Eas.DEFAULT_PROTOCOL_VERSION; 204354250c7c23b43b644961a8f241a3a3edcc9c796eMarc Blank } 2044fa088c04714957a4ebcd02a588e09878bbf4dbd4Marc Blank mProtocolVersionDouble = Double.parseDouble(mProtocolVersion); 204522e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank return true; 20467ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank } 20477ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank 20487ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank /* (non-Javadoc) 20497ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank * @see java.lang.Runnable#run() 20507ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank */ 20517ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank public void run() { 205222e927cc109f3668ef3cf2fe46feef273c7ba53eMarc Blank if (!setupService()) return; 2053ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 2054fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank try { 2055fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank SyncManager.callback().syncMailboxStatus(mMailboxId, EmailServiceStatus.IN_PROGRESS, 0); 2056fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } catch (RemoteException e1) { 2057fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank // Don't care if this fails 2058fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } 2059fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank 2060a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank // Whether or not we're the account mailbox 2061ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 20629d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank mDeviceId = SyncManager.getDeviceId(); 20637ad79c00d01a68cc0874b5fcae7c487c88b39748Marc Blank if ((mMailbox == null) || (mAccount == null)) { 2064147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank return; 20657c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (mMailbox.mType == Mailbox.TYPE_EAS_ACCOUNT_MAILBOX) { 2066a26c8a8ff1a1d26ed182ed12eb289a372e4a8bb4Marc Blank runAccountMailbox(); 2067ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 20687c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank AbstractSyncAdapter target; 20697cf921b830554e52f88a45ca4a290b17d2a1b146Marc Blank if (mMailbox.mType == Mailbox.TYPE_CONTACTS) { 20707c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank target = new ContactsSyncAdapter(mMailbox, this); 20715862a85e17e81866ca82a9905577931947fbd44eMarc Blank } else if (mMailbox.mType == Mailbox.TYPE_CALENDAR) { 20725862a85e17e81866ca82a9905577931947fbd44eMarc Blank target = new CalendarSyncAdapter(mMailbox, this); 20737cf921b830554e52f88a45ca4a290b17d2a1b146Marc Blank } else { 20747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank target = new EmailSyncAdapter(mMailbox, this); 2075ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 2076ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We loop here because someone might have put a request in while we were syncing 2077ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // and we've missed that opportunity... 2078ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank do { 2079ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (mRequestTime != 0) { 2080ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank userLog("Looping for user request..."); 2081ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mRequestTime = 0; 2082ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 2083ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank sync(target); 2084ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } while (mRequestTime != 0); 2085ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 20867531be7774769c84b499b1de5dc46da3a9468316Marc Blank } catch (EasAuthenticationException e) { 20877531be7774769c84b499b1de5dc46da3a9468316Marc Blank userLog("Caught authentication error"); 20887531be7774769c84b499b1de5dc46da3a9468316Marc Blank mExitStatus = EXIT_LOGIN_FAILURE; 2089ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (IOException e) { 2090f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank String message = e.getMessage(); 20914d14c368c1123394068c2f29caf7d3d04c56770aMarc Blank userLog("Caught IOException: ", (message == null) ? "No message" : message); 2092ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mExitStatus = EXIT_IO_ERROR; 2093ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (Exception e) { 20941431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank userLog("Uncaught exception in EasSyncService", e); 2095ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 20962fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank int status; 20972fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank 20984d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank if (!mStop) { 20998d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank userLog("Sync finished"); 21004d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank SyncManager.done(this); 21015c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank switch (mExitStatus) { 21025c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank case EXIT_IO_ERROR: 21035c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank status = EmailServiceStatus.CONNECTION_ERROR; 21045c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank break; 21055c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank case EXIT_DONE: 21065c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank status = EmailServiceStatus.SUCCESS; 21072fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank ContentValues cv = new ContentValues(); 21082fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank cv.put(Mailbox.SYNC_TIME, System.currentTimeMillis()); 21092fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank String s = "S" + mSyncReason + ':' + status + ':' + mChangeCount; 21102fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank cv.put(Mailbox.SYNC_STATUS, s); 21112fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank mContentResolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, 21122fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank mMailboxId), cv, null, null); 21135c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank break; 21145c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank case EXIT_LOGIN_FAILURE: 21155c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank status = EmailServiceStatus.LOGIN_FAILED; 21165c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank break; 211720da011530088036d2bf45d3836d6986a4b5d423Marc Blank case EXIT_SECURITY_FAILURE: 211820da011530088036d2bf45d3836d6986a4b5d423Marc Blank status = EmailServiceStatus.SECURITY_FAILURE; 21197726978228698ed6ee155723065b3c06a880ebe2Marc Blank // Ask for a new folder list. This should wake up the account mailbox; a 21207726978228698ed6ee155723065b3c06a880ebe2Marc Blank // security error in account mailbox should start the provisioning process 21217726978228698ed6ee155723065b3c06a880ebe2Marc Blank SyncManager.reloadFolderList(mContext, mAccount.mId, true); 212220da011530088036d2bf45d3836d6986a4b5d423Marc Blank break; 21235c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank default: 21245c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank status = EmailServiceStatus.REMOTE_EXCEPTION; 212577424af660458104b732bdcb718874b17d0cab3aMarc Blank errorLog("Sync ended due to an exception."); 21265c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank break; 21275c5b1184a17ceee3b585d3e3eeba414a9b08fb19Marc Blank } 21284d37107554db72f30c68e1df4a2fecd8d4b28d1cMarc Blank } else { 21298d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank userLog("Stopped sync finished."); 21302fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank status = EmailServiceStatus.SUCCESS; 21312fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank } 21322fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank 21332fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank try { 21342fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank SyncManager.callback().syncMailboxStatus(mMailboxId, status, 0); 21352fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank } catch (RemoteException e1) { 21362fc36123bfe4a3ee0f7d70052d49d08427c6d862Marc Blank // Don't care if this fails 2137fde52611eeae5ec6548f59c8e91e23bf65e6bd07Marc Blank } 21389d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank 2139c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank // Make sure SyncManager knows about this 2140c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank SyncManager.kick("sync finished"); 21419d4ac93efbba01afe668f9406feec69b3a2374ebMarc Blank } 2142ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 2143ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank} 2144